يشرح هذا الدليل كيفية دمج ميزة "متابعة المشاهدة" في تطبيق 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 الترفيه من 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")
.addPlatformSpecificPlaybackUri(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
.setDurationMills(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")
.addPlatformSpecificPlaybackUri(platformSpecificPlaybackUris)
.addPosterImages(images)
// Timestamp in millis for sample last engagement time 12/1/2023 00:00:00
.setLastEngagementTimeMillis(1701388800000)
.setDurationMills(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" أو "episode 2" أو "s1" أو "season 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")
.addPlatformSpecificPlaybackUri(platformSpecificPlaybackUris)
.addPosterImages(images)
// Timestamp in millis for sample last engagement time 12/1/2023 00:00:00
.setLastEngagementTimeMillis(1701388800000)
.setDurationMills(600000) //10 minutes in milliseconds
.setLastPlayBackPositionTimeMillis(300000) //5 minutes in milliseconds
.addContentRating(contentRating)
.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")
.addPlatformSpecificPlaybackUri(platformSpecificPlaybackUris)
.addPosterImages(images)
// Timestamp in millis for sample last engagement time 12/1/2023 00:00:00
.setLastEngagementTimeMillis(1701388800000)
.setDurationMills(72000000) //2 hours in milliseconds
.setLastPlayBackPositionTimeMillis(36000000) //1 hour in milliseconds
.addContentRating(contentRating)
.setCallToActionText("Resume")
.addTag("Live")
.build()
يمكنك اختياريًا ضبط وقت البدء أو المذيع أو رمز المذيع أو فترة التوفّر لكيان البث المباشر.
للحصول على معلومات مفصّلة عن السمات والمتطلبات، يُرجى الاطّلاع على مرجع واجهة برمجة التطبيقات.
توفير بيانات مجموعة "المتابعة"
يتولّى AppEngagePublishClient مسؤولية نشر مجموعة "المتابعة".
يمكنك استخدام طريقة publishContinuationCluste لنشر عنصر 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())
.setReason(DeleteReason.DELETE_REASON_USER_LOG_OUT)
.setSyncAcrossDevices(true)
.build()
)
الاختبار
استخدِم تطبيق التحقّق للتأكّد من أنّ عملية دمج حزمة Engage SDK تعمل بشكلٍ صحيح.
بعد استدعاء واجهة برمجة التطبيقات للنشر، تأكَّد من أنّه يتم نشر بياناتك بشكلٍ صحيح من خلال التحقّق من تطبيق التحقّق. يجب أن يتم عرض مجموعة "المتابعة" كصف منفصل ضمن واجهة التطبيق.
- اختبِر هذه الإجراءات في تطبيقك:
- سجِّل الدخول.
- بدِّل بين الملفات الشخصية(إذا كان ذلك ممكنًا).
- ابدأ تشغيل فيديو ثم أوقِفه مؤقتًا أو ارجع إلى الصفحة الرئيسية.
- أغلِق التطبيق أثناء تشغيل الفيديو.
- أزِل عنصرًا من صف "متابعة المشاهدة" (إذا كان ذلك متاحًا).
- بعد كل إجراء، تأكَّد من أنّ تطبيقك استدعى واجهة برمجة التطبيقات
publishContinuationClustersوأنّ البيانات يتم عرضها بشكلٍ صحيح في تطبيق التحقّق. يعرض تطبيق التحقّق علامة صح خضراء "كل شيء على ما يرام" للكيانات التي تم تنفيذها بشكلٍ صحيح.
الشكل 1. نجاح تطبيق التحقّق سيشير تطبيق التحقّق إلى أي كيانات بها مشاكل.
الشكل 2. خطأ في تطبيق التحقّق لتحديد المشاكل في الكيانات التي تتضمّن أخطاء وحلّها، استخدِم جهاز التحكّم عن بُعد في التلفزيون لاختيار الكيان والنقر عليه في تطبيق التحقّق. سيتم عرض المشاكل المحدّدة وتمييزها باللون الأحمر لمراجعتها (راجِع المثال أدناه).
الشكل 3. تفاصيل الخطأ في تطبيق التحقّق