book_path: /distribute/other-docs/_book.yaml project_path: /distribute/other-docs/_project.yaml
يتناول هذا الدليل كيفية دمج ميزة "مواصلة المشاهدة" في تطبيق Android TV باستخدام حزمة Engage SDK.
العمل التحضيري
أكمِل تعليمات العمل التحضيري في دليل البدء.
التكامل
إنشاء كيانات
حدّدت حزمة تطوير البرامج (SDK) عناصر مختلفة لتمثيل كل نوع من أنواع العناصر. تتيح مجموعة استمرار المحادثة الكيانات التالية:
حدِّد معرّفات الموارد الموحّدة الخاصة بالمنصة وصور الملصقات لهذه الكيانات.
عليك أيضًا إنشاء معرّفات موارد موحّدة (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 بكسل على الأقل لعرض كيان "مواصلة المشاهدة" بشكل صحيح، خاصةً ضمن Entertainment Space من 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)
.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")
.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")
.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)
.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)
.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المعدَّل.
في حال حدوث خطأ، يتم رفض الطلب بأكمله ويتم الحفاظ على الحالة الحالية.
واجهات برمجة التطبيقات الخاصة بالنشر هي واجهات برمجة تطبيقات لإجراء عمليات إدراج وتعديل، وهي تستبدل المحتوى الحالي. إذا كنت بحاجة إلى تعديل كيان معيّن في مجموعة مواصلة، عليك إعادة نشر جميع الكيانات.
يجب تقديم بيانات مجموعة المحتوى المتشابه فقط لحسابات البالغين. يجب النشر فقط عندما يكون ملف الحساب الشخصي خاصًا بشخص بالغ.
المزامنة على جميع الأجهزة
يتحكّم العلامة SyncAcrossDevices في ما إذا كانت بيانات ContinuationCluster الخاصة بالمستخدم تتم مزامنتها على جميع الأجهزة، مثل التلفزيون والهاتف والأجهزة اللوحية وما إلى ذلك. تكون المزامنة على جميع الأجهزة غير مفعّلة تلقائيًا.
القيم:
true: تتم مشاركة بيانات مجموعة المحتوى المتسلسل على جميع أجهزة المستخدم لضمان تجربة مشاهدة سلسة. ننصحك بشدة باستخدام هذا الخيار للحصول على أفضل تجربة على جميع الأجهزة.-
false: تقتصر بيانات مجموعة الاستمرار على الجهاز الحالي.
الحصول على الموافقة
يجب أن يوفّر تطبيق الوسائط إعدادًا واضحًا لتفعيل المزامنة على الأجهزة أو إيقافها. اشرح للمستخدم المزايا التي سيحصل عليها، واحفظ خياره المفضّل مرة واحدة وطبِّقه في publishContinuationCluster وفقًا لذلك.
// Example to allow cross device syncing.
client.publishContinuationCluster(
PublishContinuationClusterRequest
.Builder()
.setContinuationCluster(
ContinuationCluster.Builder()
.setAccountProfile(accountProfile)
.setSyncAcrossDevices(true)
.build()
)
.build()
)
للاستفادة إلى أقصى حدّ من ميزة "استخدام أكثر من جهاز"، تأكَّد من أنّ التطبيق يحصل على موافقة المستخدم وفعِّل SyncAcrossDevices إلى true. يتيح ذلك مزامنة المحتوى بسلاسة على جميع الأجهزة، ما يؤدي إلى تحسين تجربة المستخدم وزيادة التفاعل. على سبيل المثال، لاحظ أحد الشركاء الذين نفّذوا هذه الميزة زيادة بنسبة %40 في عدد النقرات على "متابعة المشاهدة" لأنّ المحتوى الخاص به ظهر على أجهزة متعدّدة.
حذف بيانات "الفيديوهات المقترَحة"
لحذف بيانات المستخدم يدويًا من خادم Google TV قبل فترة الاحتفاظ العادية البالغة 60 يومًا، استخدِم طريقة deleteClusters. عند تلقّي الطلب، ستحذف الخدمة جميع بيانات العثور على الفيديوهات الحالية الخاصة بملف الحساب أو الحساب بأكمله.
يحدّد تعداد 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()
)
الاختبار
استخدِم تطبيق التحقّق للتأكّد من أنّ عملية دمج حزمة تطوير البرامج (SDK) الخاصة بمنصة Engage تعمل بشكل صحيح.
بعد استدعاء واجهة برمجة التطبيقات Publish API، تأكَّد من نشر بياناتك بشكل صحيح من خلال التحقّق من تطبيق التحقّق. من المفترض أن تظهر مجموعة استمراريتك كصف منفصل ضمن واجهة التطبيق.
- اختبِر الإجراءات التالية في تطبيقك:
- سجِّل الدخول.
- التبديل بين الملفات الشخصية(إذا كان ذلك منطبقًا)
- بدء فيديو ثم إيقافه مؤقتًا أو الرجوع إلى الصفحة الرئيسية
- أغلِق التطبيق أثناء تشغيل الفيديو.
- إزالة فيلم أو برنامج تلفزيوني من صف "متابعة المشاهدة" (في حال توفُّر هذه الميزة)
- بعد كل إجراء، تأكَّد من أنّ تطبيقك قد استدعى واجهة برمجة التطبيقات
publishContinuationClustersوأنّه يتم عرض البيانات بشكل صحيح في تطبيق التحقّق. يعرض تطبيق التحقّق علامة صح خضراء "كل شيء على ما يرام" للجهات التي تم تنفيذها بشكل صحيح.
الشكل 1. نجاح عملية التحقّق من التطبيق سيشير تطبيق التحقّق إلى أي كيانات تتضمّن مشاكل.
الشكل 2. خطأ في تطبيق التحقّق لتحديد المشاكل في الكيانات التي تتضمّن أخطاءً وحلّها، استخدِم جهاز التحكّم عن بُعد الخاص بالتلفزيون لاختيار الكيان والنقر عليه في تطبيق إثبات الملكية. سيتم عرض المشاكل المحدّدة وتمييزها باللون الأحمر لتتمكّن من مراجعتها (راجِع المثال أدناه).
الشكل 3. تفاصيل خطأ تطبيق التحقّق