يتناول هذا الدليل كيفية دمج ميزة "متابعة المشاهدة" في تطبيق Android TV باستخدام حزمة Engage SDK.
العمل التحضيري
أكمِل تعليمات العمل التمهيدي في دليل البدء.
التكامل
إنشاء كيانات
حدّدت حزمة تطوير البرامج (SDK) كيانات مختلفة لتمثيل كل نوع عنصر. تتيح مجموعة المتابعة استخدام الكيانات التالية:
حدِّد معرّفات الموارد المنتظمة (URI) الخاصة بالمنصة وصور الملصقات لهذه الكيانات.
عليك أيضًا إنشاء معرّفات موارد منتظمة (URI) للتشغيل لكل نظام أساسي، مثل Android TV أو Android أو iOS، إذا لم يسبق لك ذلك. لذلك، عندما يواصل المستخدم المشاهدة على كل منصة، يستخدم التطبيق معرّف موارد منتظم (URI) مستهدَفًا لتشغيل محتوى الفيديو.
// Required. Set this when you want continue watching entities to show up on
// Google TV
val playbackUriTv = PlatformSpecificUri.Builder()
.setPlatformType(PlatformType.TYPE_ANDROID_TV)
.setActionUri(Uri.parse("https://www.example.com/entity_uri_for_tv"))
.build()
// Required. Set this when you want continue watching entities to show up on
// Google TV Android app, Entertainment Space, Playstore Widget
val playbackUriAndroid = PlatformSpecificUri.Builder()
.setPlatformType(PlatformType.TYPE_ANDROID_MOBILE)
.setActionUri(Uri.parse("https://www.example.com/entity_uri_for_android"))
.build()
// Optional. Set this when you want continue watching entities to show up on
// Google TV iOS app
val playbackUriIos = PlatformSpecificUri.Builder()
.setPlatformType(PlatformType.TYPE_IOS)
.setActionUri(Uri.parse("https://www.example.com/entity_uri_for_ios"))
.build()
val platformSpecificPlaybackUris =
Arrays.asList(playbackUriTv, playbackUriAndroid, playbackUriIos)
تتطلّب صور الملصقات معرّف موارد منتظم (URI) وأبعادًا بالبكسل (الارتفاع والعرض). استهدِف أشكال الأجهزة المختلفة من خلال تقديم صور ملصقات متعددة، ولكن تأكَّد من أنّ جميع الصور تحافظ على نسبة عرض إلى ارتفاع تبلغ 16:9 وحد أدنى للارتفاع يبلغ 200 بكسل لعرض عنصر "مواصلة المشاهدة" بشكل صحيح، خاصةً ضمن مساحة الترفيه من Google. قد لا يتم عرض الصور التي يقل ارتفاعها عن 200 بكسل.
val images = Arrays.asList(
Image.Builder()
.setImageUri(Uri.parse("http://www.example.com/entity_image1.png"))
.setImageHeightInPixel(300)
.setImageWidthInPixel(169)
.build(),
Image.Builder()
.setImageUri(Uri.parse("http://www.example.com/entity_image2.png"))
.setImageHeightInPixel(640)
.setImageWidthInPixel(360)
.build()
// Consider adding other images for different form factors
)
MovieEntity
يوضّح هذا المثال كيفية إنشاء MovieEntity مع جميع الحقول المطلوبة:
val movieEntity = MovieEntity.Builder()
.setWatchNextType(WatchNextType.TYPE_CONTINUE)
.setName("Movie name")
.addPlatformSpecificPlaybackUris(platformSpecificPlaybackUris)
.addPosterImages(images)
// Timestamp in millis for sample last engagement time 12/1/2023 00:00:00
.setLastEngagementTimeMillis(1701388800000)
// Suppose the duration is 2 hours, it is 72000000 in milliseconds
.setDurationMillis(72000000)
// Suppose last playback offset is 1 hour, 36000000 in milliseconds
.setLastPlayBackPositionTimeMillis(36000000)
.setCallToActionText("Resume")
.addTag("Action")
.build()
من خلال تقديم تفاصيل مثل الأنواع وتقييمات المحتوى، يمكن لـ Google TV عرض محتواك بطرق أكثر ديناميكية وربطه بالمشاهدين المناسبين.
val genres = Arrays.asList("Action", "Science fiction")
val rating1 = RatingSystem.Builder().setAgencyName("MPAA").setRating("PG-13").build()
val contentRatings = Arrays.asList(rating1)
val movieEntity = MovieEntity.Builder()
...
.addGenres(genres)
.addContentRatings(contentRatings)
.build()
تظل الكيانات متاحة تلقائيًا لمدة 60 يومًا ما لم تحدّد وقت انتهاء صلاحية أقصر. يجب ضبط تاريخ انتهاء صلاحية مخصّص فقط إذا كنت بحاجة إلى إزالة الكيان قبل هذه الفترة التلقائية.
// Set the expiration time to be now plus 30 days in milliseconds
val expirationTime = DisplayTimeWindow.Builder()
.setEndTimestampMillis(now().toMillis()+2592000000).build()
val movieEntity = MovieEntity.Builder()
...
.addAvailabilityTimeWindow(expirationTime)
.build()
TvEpisodeEntity
يوضّح هذا المثال كيفية إنشاء TvEpisodeEntity مع جميع الحقول المطلوبة:
val tvEpisodeEntity = TvEpisodeEntity.Builder()
.setWatchNextType(WatchNextType.TYPE_CONTINUE)
.setName("Episode name")
.addPlatformSpecificPlaybackUris(platformSpecificPlaybackUris)
.addPosterImages(images)
// Timestamp in millis for sample last engagement time 12/1/2023 00:00:00
.setLastEngagementTimeMillis(1701388800000)
.setDurationMillis(72000000) // 2 hours in milliseconds
// 45 minutes and 15 seconds in milliseconds is 2715000
.setLastPlayBackPositionTimeMillis(2715000)
.setEpisodeNumber("2")
.setSeasonNumber("1")
.setShowTitle("Title of the show")
.setCallToActionText("Resume")
.addTag("Comedy")
.build()
سيتم توسيع سلسلة رقم الحلقة (مثل "2") وسلسلة رقم الموسم (مثل "1") إلى الشكل المناسب قبل عرضها على بطاقة "مواصلة المشاهدة". يُرجى العِلم أنّها يجب أن تكون سلسلة رقمية، فلا تضع "e2" أو "الحلقة 2" أو "s1" أو "الموسم 1".
إذا كان برنامج تلفزيوني معيّن يتضمّن موسمًا واحدًا، اضبط رقم الموسم على 1.
لزيادة فرص عثور المشاهدين على المحتوى الخاص بك على Google TV، ننصحك بتقديم بيانات إضافية، مثل الأنواع وتقييمات المحتوى والفترات الزمنية للتوفّر، لأنّ هذه التفاصيل يمكن أن تحسّن خيارات العرض والفلترة.
val genres = Arrays.asList("Action", "Science fiction")
val rating1 = RatingSystem.Builder().setAgencyName("MPAA").setRating("PG-13").build()
val contentRatings = Arrays.asList(rating1)
val tvEpisodeEntity = TvEpisodeEntity.Builder()
...
.addGenres(genres)
.addContentRatings(contentRatings)
.setSeasonTitle("Season Title")
.setShowTitle("Show Title")
.build()
VideoClipEntity
في ما يلي مثال على إنشاء VideoClipEntity يتضمّن جميع الحقول المطلوبة.
يمثّل VideoClipEntity مقطعًا من إنشاء المستخدم، مثل فيديو على YouTube.
val videoClipEntity = VideoClipEntity.Builder()
.setPlayBackUri(Uri.parse("https://www.example.com/uri_for_current_platform"))
.setWatchNextType(WatchNextType.TYPE_CONTINUE)
.setName("Video clip name")
.addPlatformSpecificPlaybackUris(platformSpecificPlaybackUris)
.addPosterImages(images)
// Timestamp in millis for sample last engagement time 12/1/2023 00:00:00
.setLastEngagementTimeMillis(1701388800000)
.setDurationMillis(600000) //10 minutes in milliseconds
.setLastPlayBackPositionTimeMillis(300000) //5 minutes in milliseconds
.setCallToActionText("Resume")
.addTag("Vlog")
.build()
يمكنك اختياريًا ضبط المنشئ أو صورة المنشئ أو وقت الإنشاء بالمللي ثانية أو النافذة الزمنية للتوفّر .
LiveStreamingVideoEntity
في ما يلي مثال على إنشاء LiveStreamingVideoEntity مع جميع الحقول المطلوبة.
val liveStreamingVideoEntity = LiveStreamingVideoEntity.Builder()
.setPlayBackUri(Uri.parse("https://www.example.com/uri_for_current_platform"))
.setWatchNextType(WatchNextType.TYPE_CONTINUE)
.setName("Live streaming name")
.addPlatformSpecificPlaybackUris(platformSpecificPlaybackUris)
.addPosterImages(images)
// Timestamp in millis for sample last engagement time 12/1/2023 00:00:00
.setLastEngagementTimeMillis(1701388800000)
.setLastPlayBackPositionTimeMillis(36000000) //1 hour in milliseconds
.addContentRating(contentRating)
.setCallToActionText("Resume")
.addTag("Live")
.build()
يمكنك اختياريًا ضبط وقت البدء أو جهة البث أو رمز جهة البث أو المهلة الزمنية لتوفّر كيان البث المباشر.
للحصول على معلومات تفصيلية عن السمات والمتطلبات، يُرجى الاطّلاع على مرجع واجهة برمجة التطبيقات.
تقديم بيانات مجموعة "المتابعة"
AppEngagePublishClient مسؤول عن نشر مجموعة المتابعة.
يمكنك استخدام الإجراء publishContinuationCluster لنشر عنصر ContinuationCluster.
تأكَّد من إعداد العميل والتحقّق من توفّر الخدمة كما هو موضّح في دليل البدء.
client.publishContinuationCluster(
PublishContinuationClusterRequest
.Builder()
.setContinuationCluster(
ContinuationCluster.Builder()
.setAccountProfile(accountProfile)
.addEntity(movieEntity1)
.addEntity(movieEntity2)
.addEntity(tvEpisodeEntity1)
.addEntity(tvEpisodeEntity2)
.setSyncAcrossDevices(true)
.build()
)
.build()
)
عندما تتلقّى الخدمة الطلب، يتم تنفيذ الإجراءات التالية ضمن معاملة واحدة:
- تتم إزالة بيانات
ContinuationClusterالحالية من الشريك المطوّر. - يتم تحليل البيانات من الطلب وتخزينها في السمة
ContinuationClusterالمعدَّلة.
في حال حدوث خطأ، يتم رفض الطلب بالكامل ويتم الاحتفاظ بالحالة الحالية.
واجهات برمجة التطبيقات الخاصة بالنشر هي واجهات برمجة تطبيقات لإدراج البيانات أو تعديلها، وهي تستبدل المحتوى الحالي. إذا كنت بحاجة إلى تعديل كيان معيّن في مجموعة المتابعة، عليك نشر جميع الكيانات مرة أخرى.
يجب تقديم بيانات مجموعة المتابعة لحسابات البالغين فقط. ولا يتم نشرها إلا إذا كان الملف الشخصي للحساب يخصّ شخصًا بالغًا.
المزامنة على جميع الأجهزة
يتحكّم الإعداد setSyncAcrossDevices في ما إذا كانت بيانات ContinuationCluster الخاصة بالمستخدم تتم مزامنتها على جميع الأجهزة، مثل التلفزيون والهاتف والأجهزة اللوحية وما إلى ذلك. تكون المزامنة على جميع الأجهزة غير مفعَّلة تلقائيًا.
القيم:
true: تتم مشاركة بيانات مجموعة المتابعة على جميع أجهزة المستخدم لتقديم تجربة مشاهدة سلسة. ننصح بشدة بهذا الخيار للحصول على أفضل تجربة على جميع الأجهزة.false: تقتصر بيانات مجموعة المتابعة على الجهاز الحالي.
بالإضافة إلى ذلك، يجب تقديم AccountProfile مع رقم تعريف الحساب لكي تتم مزامنة المحتوى بين الأجهزة. يُرجى الاطّلاع على
إنشاء ملف شخصي للحساب.
الحصول على الموافقة
يجب أن يوفّر تطبيق الوسائط إعدادًا واضحًا لتفعيل ميزة المزامنة على جميع الأجهزة أو إيقافها. اشرح المزايا للمستخدم واحفظ إعداداته المفضّلة مرة واحدة وطبِّقها في "publishContinuationCluster" وفقًا لذلك.
// Example to allow cross device syncing.
client.publishContinuationCluster(
PublishContinuationClusterRequest
.Builder()
.setContinuationCluster(
ContinuationCluster.Builder()
.setAccountProfile(accountProfile)
.setSyncAcrossDevices(true)
.build()
)
.build()
)
للاستفادة إلى أقصى حد من ميزة العمل على عدة أجهزة، تأكَّد من أنّ التطبيق يحصل على
موافقة المستخدم وفعِّل SyncAcrossDevices إلى true. يتيح ذلك مزامنة المحتوى بسلاسة على جميع الأجهزة، ما يؤدي إلى تحسين تجربة المستخدم وزيادة التفاعل. على سبيل المثال، حقّق أحد الشركاء الذين نفّذوا هذه الميزة زيادة بنسبة %40 في عدد النقرات على "مواصلة المشاهدة" لأنّ محتواه ظهر على أجهزة متعددة.
حذف بيانات Engage
لحذف بيانات مستخدم يدويًا من خادم Google TV قبل فترة الاحتفاظ العادية بالبيانات التي تبلغ 60 يومًا، استخدِم طريقة deleteClusters. عند تلقّي الطلب، ستحذف الخدمة جميع بيانات Engage الحالية لملف الحساب الشخصي أو للحساب بأكمله.
يحدّد التعداد DeleteReason سبب حذف البيانات.
يزيل الرمز التالي بيانات متابعة المشاهدة عند تسجيل الخروج.
// If the user logs out from your media app, you must make the following call
// to remove continue watching data from the current google TV device,
// otherwise, the continue watching data will persist on the current
// google TV device until 60 days later.
client.deleteClusters(
DeleteClustersRequest.Builder()
.setAccountProfile(accountProfile)
.setDeleteReason(DeleteReason.DELETE_REASON_USER_LOG_OUT)
.setSyncAcrossDevices(true)
.build()
)
الاختبار
استخدِم تطبيق التحقّق للتأكّد من أنّ عملية دمج حزمة Engage SDK تعمل بشكل صحيح.
بعد استدعاء واجهة برمجة التطبيقات للنشر، تأكَّد من نشر بياناتك بشكل صحيح من خلال التحقّق من تطبيق التحقّق. من المفترض أن تظهر مجموعة الاستمرارية كصفّ منفصل في واجهة التطبيق.
- اختبِر هذه الإجراءات في تطبيقك:
- سجِّل الدخول.
- التبديل بين الملفات الشخصية(إذا كان ذلك منطبقًا)
- ابدأ تشغيل فيديو ثم أوقِفه مؤقتًا، أو ارجع إلى الصفحة الرئيسية.
- أغلِق التطبيق أثناء تشغيل الفيديو.
- إزالة عمل من صف "متابعة المشاهدة" (إذا كان ذلك متاحًا)
- بعد كل إجراء، تأكَّد من أنّ تطبيقك قد استدعى واجهة برمجة التطبيقات
publishContinuationClustersوأنّ البيانات تظهر بشكل صحيح في تطبيق التحقّق. يعرض تطبيق التحقّق علامة صح خضراء تشير إلى أنّ "كل شيء على ما يرام" للكيانات التي تم تنفيذها بشكل صحيح.
الشكل 1: Verification App Success سيشير تطبيق التحقّق إلى أي كيانات بها مشاكل.
الشكل 2. خطأ في تطبيق التحقّق لتحديد المشاكل في الكيانات التي تتضمّن أخطاء وحلّها، استخدِم جهاز التحكّم عن بُعد للتلفزيون لاختيار الكيان والنقر عليه في تطبيق التحقّق. سيتم عرض المشاكل المحدّدة وتمييزها باللون الأحمر لتتمكّن من مراجعتها (اطّلِع على المثال أدناه).
الشكل 3. تفاصيل الخطأ في تطبيق التحقّق