البدء

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

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

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

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

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

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

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

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

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

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

  3. في الرمز البرمجي، عليك إعداد حزمة تطوير البرامج (SDK) لمنصّة UMP من خلال طلب الرقم ConsentInfo::GetInstance().

    • على Android، يجب اجتياز الاختبار في JNIEnv وActivity المقدَّمَين من NDK. عليك إجراء ذلك فقط في المرة الأولى التي تتصل فيها بـ GetInstance().
    • بدلاً من ذلك، إذا كنت تستخدم حزمة تطوير البرامج (SDK) من Firebase C++ من Firebase في تطبيقك، يمكنك تمرير 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 C++ ).

يجب طلب تعديل لمعلومات موافقة المستخدم في كل مرة يتم فيها تشغيل التطبيق، وذلك باستخدام 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(). من المحتمل أن تكون قد حصلت على موافقة في الجلسة السابقة. ومن أفضل ممارسات وقت الاستجابة، عدم انتظار اكتمال معاودة الاتصال حتى تتمكن من البدء في تحميل الإعلانات في أقرب وقت ممكن بعد إطلاق تطبيقك.

إذا حدث خطأ أثناء عملية الحصول على الموافقات، يجب أن تحاول طلب الإعلانات. تستخدم حزمة تطوير البرامج لمنصّة 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);
}

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

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