مشاركة أذونات التطبيقات مع Google TV باستخدام حزمة Engage SDK

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

المتطلّبات الأساسية

يجب إعداد خلاصة إجراءات الوسائط قبل أن تتمكّن من استخدام واجهة برمجة التطبيقات device entitlement API. إذا لم يسبق لك ذلك، أكمِل عملية إعداد مهام محتوى الوسائط خلاصة.

العمل التمهيدي

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

  1. أضِف مكتبة com.google.android.engage إلى تطبيقك باتّباع الخطوات التالية:

    هناك حِزم تطوير برامج (SDK) منفصلة لاستخدامها في عملية الدمج: حزمة لتطبيقات الأجهزة الجوّالة وحزمة لتطبيقات التلفزيون.

    للأجهزة الجوّالة

    
      dependencies {
        implementation 'com.google.android.engage:engage-core:1.5.5
      }
    

    للتلفزيون

    
      dependencies {
        implementation 'com.google.android.engage:engage-tv:1.0.2
      }
    
  2. اضبط بيئة خدمة Engage على الإصدار العلني في ملف AndroidManifest.xml.

    لملف APK للأجهزة الجوّالة

    
    <meta-data
          android:name="com.google.android.engage.service.ENV"
          android:value="PRODUCTION">
    </meta-data>
    

    حزمة APK للتلفزيون

    
    <meta-data
        android:name="com.google.android.engage.service.ENV"
        android:value="PRODUCTION">
    </meta-data>
    
  3. قبل إرسال حزمة APK إلى Google، اضبط بيئة خدمة Engage على production في ملف AndroidManifest.xml. للحصول على الأداء الأمثل والتوافق المستقبلي، لا تنشر البيانات إلا عندما يكون التطبيق في المقدّمة ويتفاعل معه المستخدم بشكل نشط، مثل بدء تشغيل التطبيق أو بعد تسجيل الدخول أو أثناء الاستخدام النشط. لا يُنصح بالنشر من العمليات التي تتم في الخلفية.

  4. انشر معلومات الاشتراك في الأحداث التالية:

    1. يسجّل المستخدم الدخول إلى تطبيقك.
    2. يتبدّل المستخدم بين الملفات الشخصية (إذا كانت الملفات الشخصية متاحة).
    3. يشتري المستخدم اشتراكًا جديدًا.
    4. يُجري المستخدم ترقية لاشتراك حالي.
    5. انتهاء صلاحية اشتراك المستخدم

التكامل

يوفّر هذا القسم أمثلة الرموز البرمجية والتعليمات اللازمة لتطبيق AccountProfile وSubscriptionEntity لإدارة مختلف أنواع الاشتراكات.

حساب المستخدم وملفه الشخصي

للسماح بتوفير ميزات مخصّصة على Google TV، يُرجى تقديم معلومات الحساب. استخدِم AccountProfile لتقديم ما يلي:

  1. رقم تعريف الحساب: معرّف فريد يمثّل حساب المستخدم. ويمكن أن يكون هذا الرقم هو رقم تعريف الحساب الفعلي أو نسخة مشوّشة بشكل مناسب.
// Set the account ID to which the subscription applies.
// Don't set the profile ID because subscription applies to account level.
val accountProfile = AccountProfile.Builder()
  .setAccountId("user_account_id")
  .setProfileId("user_profile id")
  .build();

اشتراك في المستوى العادي

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

  1. نوع الاشتراك: حدِّد بوضوح خطة الاشتراك المحدّدة التي يستخدمها المستخدم.

    1. SUBSCRIPTION_TYPE_ACTIVE: لدى المستخدم اشتراك مدفوع نشط.
    2. SUBSCRIPTION_TYPE_ACTIVE_TRIAL: لدى المستخدم اشتراك تجريبي.
    3. SUBSCRIPTION_TYPE_INACTIVE: لدى المستخدم حساب ولكن ليس لديه اشتراك أو فترة تجريبية نشطة.
  2. وقت انتهاء الصلاحية: وقت اختياري بالمللي ثانية. حدِّد تاريخ انتهاء صلاحية الاشتراك.

  3. اسم حزمة موفِّر الخدمة: حدِّد اسم حزمة التطبيق الذي يدير الاشتراك.

مثال لخلاصة مقدّم الوسائط

"actionAccessibilityRequirement": [
  {
    "@type": "ActionAccessSpecification",
    "category": "subscription",
    "availabilityStarts": "2022-06-01T07:00:00Z",
    "availabilityEnds": "2026-05-31T07:00:00Z",
    "requiresSubscription": {
    "@type": "MediaSubscription",
    // Don't match this string,
    // ID is only used to for reconciliation purpose
    "@id": "https://www.example.com/971bfc78-d13a-4419",
    // Don't match this, as name is only used for displaying purpose
    "name": "Basic common name",
    "commonTier": true
  }

ينشئ المثال التالي SubscriptionEntity لمستخدم:

val subscription = SubscriptionEntity
  .Builder()
  setSubscriptionType(
    SubscriptionType.SUBSCRIPTION_TYPE_ACTIVE
  )
  .setProviderPackageName("com.google.android.example")
  // Optional
  // December 30, 2025 12:00:00AM in milliseconds since epoch
  .setExpirationTimeMillis(1767052800000)
  .build();

اشتراك Premium

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

تحتوي إذن الاستخدام هذا على الحقول التالية:

  1. المعرّف: سلسلة المعرّف المطلوبة لهذا الإذن. يجب أن تتم مطابقة هذا الرمز مع أحد معرّفات الأهلية (يُرجى العِلم أنّه ليس هو حقل المعرّف) المقدَّم في خلاصة مقدّم الوسائط المنشورة على Google TV.
  2. الاسم: هذه معلومات مساعدة وتُستخدَم لمطابقة الأذونات. على الرغم من أنّه اختياري، فإنّ تقديم اسم إذن سهل القراءة للمستخدمين يعزّز فهم أذونات المستخدمين لكلّ من المطوّرين وفرق الدعم. على سبيل المثال: Sling Orange.
  3. Expiration TimeMillis: يمكنك اختياريًا تحديد وقت انتهاء الصلاحية بالملي ثانية لهذا الإذن، إذا كان يختلف عن وقت انتهاء صلاحية الاشتراك. وستنتهي صلاحية الإذن تلقائيًا عند انتهاء صلاحية الاشتراك.

في ما يلي مثال على مقتطف خلاصة مقدّم الوسائط:

"actionAccessibilityRequirement": [
  {
    "@type": "ActionAccessSpecification",
    "category": "subscription",
    "availabilityStarts": "2022-06-01T07:00:00Z",
    "availabilityEnds": "2026-05-31T07:00:00Z",
    "requiresSubscription": {
    "@type": "MediaSubscription",
    // Don't match this string,
    // ID is only used to for reconciliation purpose
    "@id": "https://www.example.com/971bfc78-d13a-4419",

    // Don't match this, as name is only used for displaying purpose
    "name": "Example entitlement name",
    "commonTier": false,
    // match this identifier in your API. This is the crucial
    // entitlement identifier used for recommendation purpose.
    "identifier": "example.com:entitlementString1"
  }

ينشئ المثال التالي SubscriptionEntity لمستخدم مشترك:

// Subscription with entitlements.
// The entitlement expires at the same time as its subscription.
val subscription = SubscriptionEntity
  .Builder()
  .setSubscriptionType(
    SubscriptionType.SUBSCRIPTION_TYPE_ACTIVE
  )
  .setProviderPackageName("com.google.android.example")
  // Optional
  // December 30, 2025 12:00:00AM in milliseconds
  .setExpirationTimeMillis(1767052800000)
  .addEntitlement(
    SubscriptionEntitlement.Builder()
    // matches with the identifier in media provider feed
    .setEntitlementId("example.com:entitlementString1")
    .setDisplayName("entitlement name1")
    .build()
  )
  .build();
// Subscription with entitlements
// The entitement has different expiration time from its subscription
val subscription = SubscriptionEntity
  .Builder()
  .setSubscriptionType(
    SubscriptionType.SUBSCRIPTION_TYPE_ACTIVE
  )
  .setProviderPackageName("com.google.android.example")
  // Optional
  // December 30, 2025 12:00:00AM in milliseconds
  .setExpirationTimeMillis(1767052800000)
  .addEntitlement(
    SubscriptionEntitlement.Builder()
    .setEntitlementId("example.com:entitlementString1")
    .setDisplayName("entitlement name1")
    // You may set the expiration time for entitlement
    // December 15, 2025 10:00:00 AM in milliseconds
    .setExpirationTimeMillis(1765792800000)
    .build())
  .build();

اشتراك في حزمة الخدمة المرتبطة

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

يوضّح نموذج الرموز البرمجية التالي كيفية إنشاء اشتراك المستخدم.

// Subscription for linked service package
val subscription = SubscriptionEntity
  .Builder()
  .setSubscriptionType(
    SubscriptionType.SUBSCRIPTION_TYPE_ACTIVE
  )
  .setProviderPackageName("com.google.android.example")
  // Optional
  // December 30, 2025 12:00:00AM in milliseconds since epoch
  .setExpirationTimeMillis(1767052800000)
  .build();

بالإضافة إلى ذلك، إذا كان لدى المستخدم اشتراك آخر في خدمة فرعية، أضِف اشتراكًا آخر واضبط اسم حزمة الخدمة المرتبطة وفقًا لذلك.

// Subscription for linked service package
val linkedSubscription = Subscription
  .Builder()
  .setSubscriptionType(
    SubscriptionType.SUBSCRIPTION_TYPE_ACTIVE
  )
  .setProviderPackageName("linked service package name")
  // Optional
  // December 30, 2025 12:00:00AM in milliseconds since epoch
  .setExpirationTimeMillis(1767052800000)
  .addBundledSubscription(
    BundledSubscription.Builder()
      .setBundledSubscriptionProviderPackageName(
        "bundled-subscription-package-name"
      )
      .setSubscriptionType(SubscriptionType.SUBSCRIPTION_TYPE_ACTIVE)
      .setExpirationTimeMillis(111)
      .addEntitlement(
        SubscriptionEntitlement.Builder()
        .setExpirationTimeMillis(111)
        .setDisplayName("Silver subscription")
        .setEntitlementId("subscription.tier.platinum")
        .build()
      )
      .build()
  )
    .build();

يمكنك اختياريًا إضافة أذونات إلى اشتراك خدمة مرتبط أيضًا.

تقديم مجموعة الاشتراكات

شغِّل وظيفة نشر المحتوى عندما يكون التطبيق في المقدّمة.

استخدِم طريقة publishSubscriptionCluster() من فئة AppEngagePublishClient لنشر عنصر SubscriptionCluster.

استخدِم isServiceAvailable للتحقّق مما إذا كانت الخدمة متوفّرة للدمج.

client.publishSubscription(
  PublishSubscriptionRequest.Builder()
    .setAccountProfile(accountProfile)
    .setSubscription(subscription)
    .build();
  )

استخدِم setSubscription() للتحقّق من أنّه يجب أن يكون لدى المستخدم اشتراك واحد فقط في الخدمة.

استخدِم addLinkedSubscription() أو addLinkedSubscriptions() اللذَين يقبلان قائمة بالاشتراكات المرتبطة، لتفعيل اشتراك واحد أو أكثر للمستخدم.

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

تجديد الاشتراك باستمرار

  1. لتقديم آخر المعلومات فور إجراء تغييرات، يُرجى الاتصال برقم publishSubscriptionCluster() عند تغيير حالة اشتراك أحد المستخدمين، مثل التفعيل أو الإيقاف أو الترقية أو الرجوع إلى إصدار سابق.
  2. لإجراء عمليات التحقّق المنتظمة من الدقة المستمرة، يُرجى الاتصال بالفريق المعنيّ في publishSubscriptionCluster() مرة واحدة على الأقل كل شهر.

  3. لحذف بيانات "اقتراحات الفيديو"، عليك حذف بيانات المستخدم يدويًا من خادم Google TV قبل انقضاء فترة الاحتفاظ العادية التي تبلغ 60 يومًا، وذلك باستخدام الطريقة client.deleteClusters(). يؤدي ذلك إلى محو جميع بيانات اكتشاف الفيديوهات الحالية لملف التعريف الخاص بالحساب أو الحساب بأكمله استنادًا إلى DeleteReason المحدّد.

    مقتطف رمز لإزالة اشتراك المستخدم

      // If the user logs out from your media app, you must make the following call
      // to remove subscription and other video discovery data from the current
      // google TV device.
      client.deleteClusters(
        new DeleteClustersRequest.Builder()
          .setAccountProfile(
            AccountProfile
              .Builder()
              .setAccountId()
              .setProfileId()
              .build()
          )
        .setReason(DeleteReason.DELETE_REASON_USER_LOG_OUT)
        .build()
        )
      ```
    Following code snippet demonstrates removal of user subscription
    when user revokes the consent.
    
    ```Kotlin
      // If the user revokes the consent to share across device, make the call
      // to remove subscription and other video discovery data from all google
      // TV devices.
      client.deleteClusters(
        new DeleteClustersRequest.Builder()
          .setAccountProfile(
            AccountProfile
            .Builder()
            .setAccountId()
            .setProfileId()
            .build()
          )
          .setReason(DeleteReason.DELETE_REASON_LOSS_OF_CONSENT)
          .build()
      )
      ```
    
    Following code demonstrates how to remove subscription data on user profile
    deletion.
    
    ```Kotlin
    // If the user delete a specific profile, you must make the following call
    // to remove subscription data and other video discovery data.
    client.deleteClusters(
      new DeleteClustersRequest.Builder()
      .setAccountProfile(
        AccountProfile
        .Builder()
        .setAccountId()
        .setProfileId()
        .build()
      )
      .setReason(DeleteReason.DELETE_REASON_ACCOUNT_PROFILE_DELETION)
      .build()
    )
    

الاختبار

يوفّر هذا القسم دليلاً تفصيليًا لاختبار تنفيذ ميزة الاشتراك. تأكَّد من دقة البيانات وعمل الميزة بشكلٍ سليم قبل إطلاقها.

قائمة التحقّق من نشر عملية الدمج

  1. يجب أن يتم النشر عندما يكون التطبيق في المقدّمة ويتفاعل معه المستخدم بنشاط.

  2. نشر المحتوى في الحالات التالية:

    • يسجّل المستخدم الدخول لأول مرة.
    • تغيير المستخدم ملفه الشخصي (إذا كانت الملفات الشخصية متاحة)
    • يشتري المستخدم اشتراكًا جديدًا.
    • يُجري المستخدم ترقية للاشتراك.
    • انتهاء صلاحية اشتراك المستخدم
  3. تحقَّق مما إذا كان التطبيق يستدعي واجهات برمجة التطبيقات isServiceAvailable() و publishClusters() بشكلٍ صحيح في logcat، في أحداث النشر.

  4. تأكَّد من ظهور البيانات في تطبيق التحقّق. من المفترض أن يعرض تطبيق التحقّق الاشتراك في صف منفصل. عند استخدام واجهة برمجة التطبيقات لنشر البيانات، من المفترض أن تظهر البيانات في تطبيق التحقّق.

    • تأكَّد من أنّ علامة خدمة Engage لم يتم ضبطها على وضع الإنتاج فيملف بيان Android الخاص بالتطبيق.
    • ثبِّت تطبيق Engage Verification وافتحه.
    • إذا كانت قيمة isServiceAvailable هي false في تطبيق التحقّق، انقر على الزر Toggle في تطبيق التحقّق لضبطها على true.
    • أدخِل اسم حزمة التطبيق، وسيظهر تلقائيًا البيانات المنشورة.
  5. انتقِل إلى التطبيق ونفِّذ كلّ إجراء من الإجراءات التالية:

    • سجِّل الدخول.
    • التبديل بين الملفات الشخصية (إذا كان ذلك متاحًا)
    • شراء اشتراك جديد
    • ترقية اشتراك حالي
    • انتهاء صلاحية الاشتراك

التحقّق من الدمج

لاختبار عملية الدمج، استخدِم تطبيق التحقّق.

تطبيق التحقّق هو تطبيق Android يمكن للمطوّرين استخدامه للتحقّق مما إذا كان الدمج يعمل بشكل صحيح. يتضمّن التطبيق إمكانات لمساعدة المطوّرين في التحقّق من البيانات وبث النوايا. ويساعد ذلك في التحقّق من دقة البيانات ووظائفها المناسبة قبل الإطلاق.

  1. لكل حدث، تحقّق مما إذا كان التطبيق قد استدعى publishSubscription واجهة برمجة التطبيقات. تحقَّق من البيانات المنشورة في تطبيق التحقّق. تحقَّق من أنّ كل شيء يظهر باللون الأخضر في تطبيق التحقّق.
  2. إذا كانت جميع معلومات العنصر صحيحة، ستظهر علامة اختيار خضراء "كلّ شيء على ما يرام" في جميع العناصر.

    لقطة شاشة لنجاح إثبات الملكية في التطبيق
    الشكل 1. الاشتراك الناجح
  3. يتم أيضًا تمييز المشاكل في تطبيق إثبات الملكية.

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

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

    لقطة شاشة لخطأ في تطبيق التحقّق
    الشكل 4: تفاصيل خطأ الاشتراك