البدء

بموجب سياسة موافقة المستخدم في الاتحاد الأوروبي التي تتّبعها Google، يجب الإفصاح عن معلومات معيّنة للمستخدمين في المنطقة الاقتصادية الأوروبية (EEA) وفي المملكة المتحدة والحصول على موافقتهم على استخدام ملفات تعريف الارتباط أو غير ذلك من البيانات المحفوظة على أجهزتهم المحلية، حيثما يكون ذلك مطلوبًا بموجب القانون، وعلى استخدام البيانات الشخصية (مثل AdID) لعرض الإعلانات. تعكس هذه السياسة شروط "التوجيه الأوروبي بشأن الخصوصية الإلكترونية" و"اللائحة العامة لحماية البيانات" (GDPR).

لمساعدة الناشرين في الوفاء بالتزاماتهم بموجب هذه السياسة، تقدِّم Google حزمة تطوير البرامج (SDK) لمنصّة User Messaging Platform (UMP). تم تحديث حزمة تطوير البرامج (SDK) الخاصة بمنصة UMP لتتوافق مع أحدث معايير IAB. يمكن الآن التعامل مع كل هذه الإعدادات بسهولة في AdMob الخصوصية والمراسلة.

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

  • المستوى 21 من واجهة برمجة تطبيقات Android أو مستوى أعلى (لنظام التشغيل Android)

إنشاء نوع رسالة

أنشئ رسائل للمستخدمين باستخدام أحد أنواع رسائل المستخدمين المتاحة ضمن علامة التبويب الخصوصية والمراسلة في AdMob . تحاول حزمة تطوير البرامج (SDK) لمنصة UMP عرض رسالة مستخدم تم إنشاؤها من مجموعة AdMob معرّف التطبيق في مشروعك. فإذا لم يتم تهيئة أي رسالة لتطبيقك، فستعرض حزمة SDK رسالة خطأ.

لمزيد من التفاصيل، يُرجى الاطّلاع على لمحة عن الخصوصية والمراسلة.

تثبيت حزمة تطوير البرامج (SDK)

  1. اتّبِع خطوات تثبيت حزمة تطوير البرامج C++ لإعلانات Google على الأجهزة الجوّالة. يتم تضمين حزمة تطوير البرامج (SDK) الخاصة بمنصة UMP C++ في حزمة تطوير البرامج (SDK) الخاصة بإصدار "GMA C++".

  2. تأكّد من ضبط رقم تعريف تطبيق AdMob لتطبيقك في المشروع قبل المتابعة.

  3. في الرمز، أنشِئ حزمة تطوير البرامج لمنصة UMP من خلال طلب الرمز ConsentInfo::GetInstance().

    • على Android، يجب تمرير JNIEnv وActivity المقدّمة من NDK. ما عليك سوى تنفيذ هذا الإجراء في المرة الأولى التي تتصل فيها بـ GetInstance().
    • بدلاً من ذلك، إذا كنت تستخدم حزمة تطوير البرامج (SDK) الخاصة بمنصة Firebase C++ في تطبيقك، يمكنك إدخال firebase::App في المرة الأولى التي يتم فيها الاتصال بـ GetInstance().
    #include "firebase/gma/ump.h"
    
    namespace ump = ::firebase::gma::ump;
    
    // Initialize using a firebase::App
    void InitializeUserMessagingPlatform(const firebase::App& app) {
      ump::ConsentInfo* consent_info = ump::ConsentInfo::GetInstance(app);
    }
    
    // Initialize without a firebase::App
    #ifdef ANDROID
    void InitializeUserMessagingPlatform(JNIEnv* jni_env, jobject activity) {
      ump::ConsentInfo* consent_info = ump::ConsentInfo::GetInstance(jni_env, activity);
    }
    #else  // non-Android
    void InitializeUserMessagingPlatform() {
      ump::ConsentInfo* consent_info = ump::ConsentInfo::GetInstance();
    }
    #endif
    

تعرض جميع الطلبات اللاحقة إلى ConsentInfo::GetInstance() النسخة نفسها.

إذا انتهيت من استخدام حزمة تطوير البرامج (SDK) لمنصّة UMP، يمكنك إيقاف حزمة تطوير البرامج (SDK) من خلال حذف النسخة الافتراضية ConsentInfo:

void ShutdownUserMessagingPlatform() {
  ump::ConsentInfo* consent_info = ump::ConsentInfo::GetInstance();
  delete consent_info;
}

استخدام Future لمراقبة العمليات غير المتزامنة

توفِّر لك firebase::Future طريقة لتحديد حالة اكتمال استدعاءات الطُرق غير المتزامنة.

جميع دوال UMP C++ وطلبات الطُرق التي تعمل بشكل غير متزامن تعرض Future، وتوفّر أيضًا وظيفة "النتيجة الأخيرة" لاسترداد Future من أحدث عملية.

هناك طريقتان للحصول على نتيجة من Future:

  1. يمكنك استدعاء OnCompletion()، ليتم تمرير وظيفة معاودة الاتصال لديك، والتي يتم استدعاؤها عند اكتمال العملية.
  2. تحقَّق بشكل دوري من status() الخاصة بـ Future. عندما تتغير الحالة من kFutureStatusPending إلى kFutureStatusCompleted، تكون العملية قد اكتملت.

بعد اكتمال العملية غير المتزامنة، يجب التحقّق من error() في Future للحصول على رمز الخطأ للعملية. إذا كان رمز الخطأ هو 0 (kConsentRequestSuccess أو kConsentFormSuccess)، يعني ذلك اكتمال العملية بنجاح. بخلاف ذلك، تحقَّق من رمز الخطأ وerror_message() لتحديد الخطأ الذي حدث.

معاودة الاتصال بالإكمال

في ما يلي مثال على كيفية استخدام OnCompletion لضبط معاودة الاتصال بالإكمال، والذي يتم استدعاؤه عند اكتمال العملية غير المتزامنة.

void MyApplicationStart() {
  // [... other app initialization code ...]

  ump::ConsentInfo *consent_info = ump::ConsentInfo::GetInstance();

  // See the section below for more information about RequestConsentInfoUpdate.
  firebase::Future<void> result = consent_info->RequestConsentInfoUpdate(...);

  result.OnCompletion([](const firebase::Future<void>& req_result) {
    if (req_result.error() == ump::kConsentRequestSuccess) {
      // Operation succeeded. You can now call LoadAndShowConsentFormIfRequired().
    } else {
      // Operation failed. Check req_result.error_message() for more information.
    }
  });
}

تعديل الاستطلاع الحلقي

في هذا المثال، بعد بدء عملية غير متزامنة عند تشغيل التطبيق، يتم التحقّق من النتائج في مكان آخر، ضِمن دالة حلقة تحديث اللعبة (التي تعمل مرة واحدة لكل إطار).

ump::ConsentInfo *g_consent_info = nullptr;
bool g_waiting_for_request = false;

void MyApplicationStart() {
  // [... other app initialization code ...]

  g_consent_info = ump::ConsentInfo::GetInstance();
  // See the section below for more information about RequestConsentInfoUpdate.
  g_consent_info->RequestConsentInfoUpdate(...);
  g_waiting_for_request = true;
}

// Elsewhere, in the game's update loop, which runs once per frame:
void MyGameUpdateLoop() {
  // [... other game logic here ...]

  if (g_waiting_for_request) {
    // Check whether RequestConsentInfoUpdate() has finished.
    // Calling "LastResult" returns the Future for the most recent operation.
    firebase::Future<void> result =
      g_consent_info->RequestConsentInfoUpdateLastResult();

    if (result.status() == firebase::kFutureStatusComplete) {
      g_waiting_for_request = false;
      if (result.error() == ump::kConsentRequestSuccess) {
        // Operation succeeded. You can call LoadAndShowConsentFormIfRequired().
      } else {
        // Operation failed. Check result.error_message() for more information.
      }
    }
  }
}

للحصول على مزيد من المعلومات حول "firebase::Future"، يمكنك الاطّلاع على مستندات حزمة تطوير البرامج (SDK) الخاصة بمنصة Firebase C++ ومستندات حزمة "SDK لإعلانات Google على الأجهزة الجوّالة" (GMA).

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

#include "firebase/gma/ump.h"

namespace ump = ::firebase::gma::ump;

void MyApplicationStart() {
  ump::ConsentInfo* consent_info = ump::ConsentInfo::GetInstance();

  // Create a ConsentRequestParameters struct.
  ump::ConsentRequestParameters params;
  // Set tag for under age of consent. False means users are NOT under age
  // of consent.
  params.tag_for_under_age_of_consent = false;

  consent_info->RequestConsentInfoUpdate(params).OnCompletion(
    [](const Future<void>& result) {
      if (result.error() != ump::kConsentRequestSuccess) {
        LogMessage("Error requesting consent update: %s", result.error_message());
      } else {
        // Consent status is now available.
      }
    });
}

انظر أعلاه للحصول على مثال على التحقق من الاكتمال باستخدام استطلاع تكرار التحديث بدلاً من معاودة الاتصال بالإكمال.

تحميل نموذج موافقة وعرضه إذا لزم الأمر

بعد حصولك على أحدث حالة للموافقة، تواصَل مع LoadAndShowConsentFormIfRequired() الصف ConsentInfo لتحميل نموذج موافقة. إذا كانت حالة الموافقة مطلوبة، تُحمِّل حزمة تطوير البرامج (SDK) نموذجًا وتعرضه على الفور من FormParentالتي تم توفيرها. يتم استدعاء Future مكتمل بعد إغلاق النموذج. في حال لم تكن الموافقة مطلوبة، يتم طلب Future إكمال على الفور.

void MyApplicationStart(ump::FormParent parent) {
  ump::ConsentInfo* consent_info = ump::ConsentInfo::GetInstance();

  // Create a ConsentRequestParameters struct..
  ump::ConsentRequestParameters params;
  // Set tag for under age of consent. False means users are NOT under age of consent.
  params.tag_for_under_age_of_consent = false;

  consent_info->RequestConsentInfoUpdate(params).OnCompletion(
    [*](const Future<void>& req_result) {
      if (req_result.error() != ump::kConsentRequestSuccess) {
        // req_result.error() is a kConsentRequestError enum.
        LogMessage("Error requesting consent update: %s", req_result.error_message());
      } else {
        consent_info->LoadAndShowConsentFormIfRequired(parent).OnCompletion(
        [*](const Future<void>& form_result) {
          if (form_result.error() != ump::kConsentFormSuccess) {
            // form_result.error() is a kConsentFormError enum.
            LogMessage("Error showing consent form: %s", form_result.error_message());
          } else {
            // Either the form was shown and completed by the user, or consent was not required.
          }
        });
      }
    });
}

إذا كنت بحاجة إلى تنفيذ أي إجراءات بعد أن يختار المستخدم النموذج أو يرفضه، ضَع هذا المنطق في الرمز الذي يعالج Future الذي تم عرضه في LoadAndShowConsentFormIfRequired().

طلب إدراج الإعلانات

قبل طلب الإعلانات في تطبيقك، تحقّق مما إذا حصلت على موافقة من المستخدِم الذي يستخدم ConsentInfo::GetInstance()‑>CanRequestAds(). هناك مكانان يمكن التحقق منهما أثناء جمع الموافقات:

  1. بعد الحصول على الموافقة في الجلسة الحالية
  2. مباشرة بعد الاتصال بـ RequestConsentInfoUpdate(). من المحتمَل أنّه تم الحصول على موافقة في الجلسة السابقة. من بين أفضل الممارسات التي يجب اتّباعها لوقت الاستجابة، عدم انتظار اكتمال معاودة الاتصال حتى تتمكن من البدء في تحميل الإعلانات في أقرب وقت ممكن بعد إطلاق التطبيق.

وفي حال حدوث خطأ أثناء عملية الحصول على الموافقات، عليك مع ذلك محاولة طلب الإعلانات. تستخدم حزمة تطوير البرامج (SDK) لمنصة UMP حالة الموافقة من الجلسة السابقة.

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

#include "firebase/future.h"
#include "firebase/gma/gma.h"
#include "firebase/gma/ump.h"

namespace gma = ::firebase::gma;
namespace ump = ::firebase::gma::ump;
using firebase::Future;

ump::ConsentInfo* g_consent_info = nullptr;
// State variable for tracking the UMP consent flow.
enum { kStart, kRequest, kLoadAndShow, kInitGma, kFinished, kErrorState } g_state = kStart;
bool g_ads_allowed = false;

void MyApplicationStart() {
  g_consent_info = ump::ConsentInfo::GetInstance(...);

  // Create a ConsentRequestParameters struct..
  ump::ConsentRequestParameters params;
  // Set tag for under age of consent. False means users are NOT under age of consent.
  params.tag_for_under_age_of_consent = false;

  g_consent_info->RequestConsentInfoUpdate(params);
  // CanRequestAds() can return a cached value from a previous run immediately.
  g_ads_allowed = g_consent_info->CanRequestAds();
  g_state = kRequest;
}

// This function runs once per frame.
void MyGameUpdateLoop() {
  // [... other game logic here ...]

  if (g_state == kRequest) {
    Future<void> req_result = g_consent_info->RequestConsentInfoUpdateLastResult();

    if (req_result.status() == firebase::kFutureStatusComplete) {
      g_ads_allowed = g_consent_info->CanRequestAds();
      if (req_result.error() == ump::kConsentRequestSuccess) {
        // You must provide the FormParent (Android Activity or iOS UIViewController).
        ump::FormParent parent = GetMyFormParent();
        g_consent_info->LoadAndShowConsentFormIfRequired(parent);
        g_state = kLoadAndShow;
      } else {
        LogMessage("Error requesting consent status: %s", req_result.error_message());
        g_state = kErrorState;
      }
    }
  }
  if (g_state == kLoadAndShow) {
    Future<void> form_result = g_consent_info->LoadAndShowConsentFormIfRequiredLastResult();

    if (form_result.status() == firebase::kFutureStatusComplete) {
      g_ads_allowed = g_consent_info->CanRequestAds();
      if (form_result.error() == ump::kConsentRequestSuccess) {
        if (g_ads_allowed) {
          // Initialize GMA. This is another asynchronous operation.
          firebase::gma::Initialize();
          g_state = kInitGma;
        } else {
          g_state = kFinished;
        }
        // Optional: shut down the UMP SDK to save memory.
        delete g_consent_info;
        g_consent_info = nullptr;
      } else {
        LogMessage("Error displaying consent form: %s", form_result.error_message());
        g_state = kErrorState;
      }
    }
  }
  if (g_state == kInitGma && g_ads_allowed) {
    Future<gma::AdapterInitializationStatus> gma_future = gma::InitializeLastResult();

    if (gma_future.status() == firebase::kFutureStatusComplete) {
      if (gma_future.error() == gma::kAdErrorCodeNone) {
        g_state = kFinished;
        // TODO: Request an ad.
      } else {
        LogMessage("Error initializing GMA: %s", gma_future.error_message());
        g_state = kErrorState;
      }
    }
  }
}

خيارات الخصوصية

تتطلّب بعض نماذج الموافقة من المستخدم تعديل موافقته في أي وقت. التزم بالخطوات التالية لعرض زر خيارات الخصوصية إذا لزم الأمر.

ولإجراء ذلك:

  1. نفِّذ عنصر واجهة مستخدم، مثل زر في صفحة إعدادات تطبيقك، يمكن أن يؤدي إلى ظهور نموذج خيارات الخصوصية.
  2. بعد LoadAndShowConsentFormIfRequired() الاكتمال، ضَع علامة في المربّع getPrivacyOptionsRequirementStatus() لتحديد ما إذا كان سيتم عرض عنصر واجهة المستخدم الذي يمكنه عرض نموذج خيارات الخصوصية.
  3. عندما يتفاعل مستخدم مع عنصر في واجهة المستخدم، يمكنك استدعاء showPrivacyOptionsForm() لعرض النموذج حتى يتمكّن المستخدم من تعديل خيارات الخصوصية في أي وقت.

الاختبار

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

  1. الاتصال RequestConsentInfoUpdate()
  2. تحقق من مخرجات السجلّ الخاصة برسالة مشابهة للمثال التالي والتي تُظهر رقم تعريف جهازك وكيفية إضافته كجهاز اختبار:

    Android

    Use new ConsentDebugSettings.Builder().addTestDeviceHashedId("33BE2250B43518CCDA7DE426D04EE231")
    to set this as a debug device.
    

    iOS

    <UMP SDK>To enable debug mode for this device,
    set: UMPDebugSettings.testDeviceIdentifiers = @[2077ef9a63d2b398840261c8221a0c9b]
    
  3. انسخ رقم تعريف جهاز الاختبار إلى الحافظة.

  4. يُرجى تعديل الرمز الخاص بك لضبط ConsentRequestParameters.debug_settings.debug_device_ids قائمة بأرقام تعريف الأجهزة الاختبارية.

    void MyApplicationStart() {
      ump::ConsentInfo consent_info = ump::ConsentInfo::GetInstance(...);
    
      ump::ConsentRequestParameters params;
      params.tag_for_under_age_of_consent = false;
      params.debug_settings.debug_device_ids = {"TEST-DEVICE-HASHED-ID"};
    
      consent_info->RequestConsentInfoUpdate(params);
    }
    

فرض منطقة جغرافية

توفّر حزمة تطوير البرامج (SDK) لمنصة UMP طريقة لاختبار سلوك تطبيقك كما لو كان الجهاز مقيمًا في المنطقة الاقتصادية الأوروبية أو المملكة المتحدة باستخدام ConsentRequestParameters.debug_settings.debug_geography. تجدر الإشارة إلى أنّ إعدادات تصحيح الأخطاء لا تعمل إلا على الأجهزة الاختبارية.

void MyApplicationStart() {
  ump::ConsentInfo consent_info = ump::ConsentInfo::GetInstance(...);

  ump::ConsentRequestParameters params;
  params.tag_for_under_age_of_consent = false;
  params.debug_settings.debug_device_ids = {"TEST-DEVICE-HASHED-ID"};
  // Geography appears as EEA for debug devices.
  params.debug_settings.debug_geography = ump::kConsentDebugGeographyEEA

  consent_info->RequestConsentInfoUpdate(params);
}

عند اختبار تطبيقك باستخدام حزمة تطوير البرامج لمنصة UMP، قد يكون من المفيد إعادة ضبط حالة حزمة تطوير البرامج (SDK) كي تتمكّن من محاكاة تجربة التثبيت الأولى للمستخدم. توفر حزمة SDK الطريقة Reset() لإجراء ذلك.

  ConsentInfo::GetInstance()->Reset();