[Unity] 1. consentmanager SDK Integration
The Consent Management Platform (CMP) Unity Plugin allows you to easily integrate Consent Management functionality into your Unity applications for handling user consent and privacy preferences.
Features
- Supports both iOS and Android platforms.
- Provides a bridge between Unity and native platform-specific CMP functionalities.
- Allows you to initialize, manage user consent, and handle privacy-related data.
Getting Started
Installation
-
- Download the latest release of the plugin.
- Import the package into your Unity project using Assets > Import Package > Custom Package.
Usage
Follow these steps to start using the plugin.
Initialization: To use the CMP functionality, initialize the CMPManager instance.
CmpManager.Instance.Initialize(domain, codeId, appName, language);
For domain
use the Server-Domain found in your consentmanager account under Menu > CMPs > Get Codes for Apps. For codeID
use the Code-ID found on the same page in your consentmanger account. The fields appName
and language
can be set by you.
Consent Layer: Display the consent layer using the following:
CmpManager.Instance.OpenConsentLayer();
Check Consent: Check if the user has given consent:
bool hasConsent = CmpManager.Instance.HasConsent();
Callbacks: Set callback listeners for various events:
CmpManager.Instance.AddEventListeners(OnOpen, OnClose, OnNotOpened, OnCmpButtonClicked, OnError);
Purpose and Vendor Checks: Check for consent related to specific purposes and vendors:
bool hasPurpose = CmpManager.Instance.HasPurpose(id);
bool hasVendor = CmpManager.Instance.HasVendor(id);
Export Data: Export CMP data:
string cmpString = CmpManager.Instance.ExportCmpString();
Config JSON
In the CmpSdkConfig.json you can set the native SDK Version for iOS and Android which will be used for the build process:
Find the compatible native SDK Versions here:
{
"displayName": "Consentmanager SDK",
"name": "CmpSdk",
"androidBasePath": "net.consentmanager.sdk",
"version": "1.0.0",
"androidLibraryVersion": "x.xx.x",
"iosLibraryVersion": "x.xx.x",
"description": "Unity plugin helps you to use native Consentmanager functionality on Android and iOS."
}
Build Settings
To change the build settings go to Window
-> CmpSdk
iOS Build Settings
- Enable iOS Build Script: Toggle this to enable or disable the build script responsible for integrating the iOS SDK into the Unity project.
- xcFramework Path: Specify the path to the xcFramework directory. This path can be edited directly or browsed to using the accompanying button.
- Include Version Tag: When enabled, appends the iOS SDK version to the xcFramework path, allowing for version-specific configurations.
- Resulting Framework Path: Displays the fully resolved path to the xcFramework, including the SDK version if the version tag is included.
- Enable App Tracking Transparency: Toggle this to enable the App Tracking Transparency feature for iOS, which is necessary for user consent under iOS privacy guidelines.
- App Tracking Transparency Consent Message: A text field to input the custom message displayed to users when requesting consent for tracking. This message should be clear and concise, explaining why the consent is needed.
Android Build Settings
- Enable Android Build Script: Toggle to enable or disable the build script for integrating the Android SDK into the Unity project.
-
Integrate Custom Layout: When enabled, this allows the use of a custom layout for the consent layer for the fragment UIView.
If you are interested to use a custom layout with fragments, make sure that your Unity Project adds the appcompat dependency. Add a custom main Template
Assets/Plugins/Android/mainTemplate.gradle
and add the dependency:
implementation 'androidx.appcompat:appcompat:1.x.x'
Event Listener
Callback Event | Description | Parameters Passed |
---|---|---|
OnOpen | Triggered when the CMP consent tool is opened. | None |
OnClose | Triggered when the CMP consent tool is closed. | None |
OnNotOpened | Triggered if the CMP consent tool fails to open. | None |
OnCmpButtonClicked | Triggered when a button within the CMP consent tool is clicked. | CmpButtonEvent buttonEvent |
OnError | Triggered when an error occurs within the CMP consent tool. |
CmpErrorType errorType , string message
|
OnGoogleConsentUpdated | Triggered when the Google consent mode status is updated. | CmpGoogleConsentModeStatus status |
OnCmpATTrackingStatusChanged (iOS only) | Triggered when the App Tracking Transparency status changes. |
ATTrackingManagerAuthorizationStatus oldStatus , ATTrackingManagerAuthorizationStatus newStatus , double lastUpdated
|
Custom Layout
Unity supports different custom layouts:
public enum ScreenConfig
{
FullScreen,
HalfScreenBottom,
HalfScreenTop,
CenterScreen,
SmallCenterScreen,
LargeTopScreen,
LargeBottomScreen,
}
Example usage:
_cmpConfig = new CmpConfig(CodeId, Domain, AppName, Language)
{
Debug = true,
Timeout = 8000
};
_cmpConfig.UIConfig.screenConfig = (CmpUIConfig.ScreenConfig) Enum.Parse(typeof(CmpUIConfig.ScreenConfig), s);
_cmpManager.SetUIConfig(_cmpConfig.UIConfig);
Google Consent Mode
Reference documentation : Get started with Google Firebase for Unity
Integration : Unity Setup
To use the google consent mode the CMP Unity SDK supports an interface to set the google consent status:
// public class CmpSampleScript : MonoBehaviour, IOnCmpGoogleConsentUpdatedCallback { ...
// Make sure to implement the Interface IOnCmpGoogleConsentUpdatedCallback
public void OnGoogleConsentUpdated(CmpGoogleConsentModeStatus status)
{
// Convert CmpGoogleConsentModeStatus to Firebase compatible dictionary
var firebaseConsentDict = new Dictionary<ConsentType, ConsentStatus>();
foreach (var consent in status.ConsentDictionary)
{
// Convert GoogleConsentType to Firebase ConsentType
var firebaseConsentType = ConvertToFirebaseConsentType(consent.Key);
// Convert GoogleConsentStatus to Firebase ConsentStatus
var firebaseConsentStatus = ConvertToFirebaseConsentStatus(consent.Value);
firebaseConsentDict[firebaseConsentType] = firebaseConsentStatus;
}
// Apply the consent settings to Firebase Analytics
FirebaseAnalytics.SetConsent(firebaseConsentDict);
AppendLog($"Google Consent Mode: {firebaseConsentDict}");
}
private static ConsentType ConvertToFirebaseConsentType(GoogleConsentType googleConsentType)
{
return googleConsentType switch
{
GoogleConsentType.AnalyticsStorage => ConsentType.AnalyticsStorage,
GoogleConsentType.AdStorage => ConsentType.AdStorage,
GoogleConsentType.AdUserData => ConsentType.AdUserData,
GoogleConsentType.AdPersonalization => ConsentType.AdPersonalization,
_ => throw new InvalidEnumArgumentException($"Unknown GoogleConsentType: {googleConsentType}")
};
}
private static ConsentStatus ConvertToFirebaseConsentStatus(GoogleConsentStatus googleConsentStatus)
{
return googleConsentStatus switch
{
GoogleConsentStatus.Granted => ConsentStatus.Granted,
GoogleConsentStatus.Denied => ConsentStatus.Denied,
_ => throw new InvalidEnumArgumentException($"Unknown GoogleConsentStatus: {googleConsentStatus}")
};
}
in this example the OnGoogleConsentUpdate callback is called when the user gives a consent.
FirebaseAnalytics.SetConsent(firebaseConsentDict);
In this line the Consent Status is set to Firebase Analytics. The other two functions are mapping the CMP Consent Status to the Google Consent Types and Status.
Roadmap for future features
- disableVendorList, enableVendorList
- disablePurposeList, enablePurposeList
Compatibility
- Unity 20XX.X.X or later
- iOS (via DllImport)
- Android (via JNI)
Support
For bug reports, feature requests, or general inquiries, please open an issue on the repository.
Credits
Created and maintained by Skander Ben Abdelmalak.
Example Script
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Threading;
using CmpSdk.Callbacks;
#if UNITY_IOS
using CmpSdk.Delegates;
#endif
using CmpSdk.Models;
using Firebase.Analytics;
using Firebase.Extensions;
using UnityEngine;
using FirebaseApp = Firebase.FirebaseApp;
namespace CmpSdk.Samples.Scripts
{
// V prefix for Vendor
public static class Vendors
{
public const string GoogleAnalytics = "S26";
public const string AmazonAD = "793";
public const string Facebook = "S7";
public const string S1 = "S1";
}
// P prefix for Purpose
public static class Purposes
{
public const string P1 = "1";
public const string Marketing = "C2";
public const string Technical = "S2";
public const string Security = "S1";
}
public class CmpSampleScript : MonoBehaviour, IOnOpenCallback, IOnCloseCallback, IOnCmpNotOpenedCallback,
IOnCmpButtonClickedCallback, IOnErrorCallback, IOnCmpGoogleConsentUpdatedCallback
{
readonly List<string> _purposeList = new() { Purposes.P1, Purposes.Marketing, Purposes.Technical, Purposes.Security };
readonly List<string> _vendorList = new() { Vendors.S1, Vendors.GoogleAnalytics, Vendors.AmazonAD, Vendors.Facebook };
// UI elements
private string _idPurposeOrVendorInputField;
private string _importStringInputField;
[SerializeField] private CmpUIManager uiManager;
// CmpManager Instance
private CmpConfig _cmpConfig;
private CmpManager _cmpManager;
private Thread _mainThread;
// Configuration constants
private const string CodeId = "TOOD Your CMP Code ID";
private const string Domain = "delivery.consentmanager.net";
private const string AppName = "UnityExample";
private const string Language = "DE";
private FirebaseApp _app;
private void Awake()
{
if (!Application.isPlaying)
{
Debug.Log("Application is not playing.");
return;
}
FirebaseApp.CheckAndFixDependenciesAsync().ContinueWithOnMainThread(task => {
var dependencyStatus = task.Result;
if (dependencyStatus == Firebase.DependencyStatus.Available) {
// Create and hold a reference to your FirebaseApp,
// where app is a Firebase.FirebaseApp property of your application class.
_app = FirebaseApp.DefaultInstance;
// Set a flag here to indicate whether Firebase is ready to use by your app.
} else {
Debug.LogError($"Could not resolve all Firebase dependencies: {dependencyStatus}");
// Firebase Unity SDK is not safe to use here.
}
});
_cmpManager = CmpManager.Instance;
}
private void Start()
{
_cmpConfig = new CmpConfig(CodeId, Domain, AppName, Language)
{
Debug = true,
Timeout = 8000
};
#if UNITY_ANDROID
_cmpConfig.UIConfig.isOutsideTouchable = true;
_cmpConfig.UIConfig.SetAndroidUiType(AndroidUiType.Dialog);
#endif
// Initialize Consent Manager
InitializeCmpManager();
// Initialize UI buttons
InitializeUIButtons();
// Launch Consent Manager
_cmpManager.Launch();
}
private void InitializeUIButtons()
{
uiManager.CreateButtons(
new ButtonData("Open", OnClickOpenConsentLayer),
new ButtonData("Check", OnClickOpenConsentLayerOnCheck),
new ButtonData("Check?", OnclickCheckConsentRequired),
new ButtonData("ATT?", OnClickRequestATTrackingStatus),
new ButtonData("Get Status", OnClickDebugConsentStatus),
new ButtonData("Initialize", OnClickInitialize),
new ButtonData("Accept All", OnClickAcceptAll),
new ButtonData("Reject All", OnClickRejectAll),
new ButtonData("Reset", OnClickResetConsentData),
new ButtonData("Import", OnClickImportCmpString),
new ButtonData("Export", OnClickExportCmpString)
);
uiManager.CreateDropdown("Screen Config", GetScreenConfigOptions(), s =>
{
AppendLog($"Set Screen ${s}");
_cmpConfig.UIConfig.screenConfig = (CmpUIConfig.ScreenConfig) Enum.Parse(typeof(CmpUIConfig.ScreenConfig), s);
_cmpManager.SetUIConfig(_cmpConfig.UIConfig);
});
uiManager.CreateDropdown("Purposes", _purposeList, s =>
{
_idPurposeOrVendorInputField = s;
CheckHasPurpose(s);
});
uiManager.CreateDropdown("Vendors", _vendorList, s =>
{
_idPurposeOrVendorInputField = s;
CheckHasVendor(s);
});
}
private void InitializeCmpManager()
{
_mainThread = Thread.CurrentThread;
AppendLog("Consentmanager SampleScene started");
#if (UNITY_ANDROID || UNITY_IOS) && !UNITY_EDITOR
_cmpManager.Initialize(_cmpConfig);
_cmpManager.AddGoogleConsentModeListener(OnGoogleConsentUpdated);
_cmpManager.AddEventListeners(OnOpen, OnClose, OnNotOpened, OnCmpButtonClicked, OnError);
#endif
#if UNITY_IOS
CmpATTrackingManager.EnableAutomaticATTracking();
CmpATTrackingManager.Instance.RegisterOnATTrackingStatusChangedListener(OnCmpATTrackingStatusChanged);
#endif
}
#region Button Events
private void OnClickInitialize()
{
AppendLog("Initialize");
_cmpManager.Launch();
}
private async void OnClickRejectAll()
{
AppendLog("Calling Reject All");
await _cmpManager.RejectAll();
AppendLog("Rejected All");
}
private async void OnClickAcceptAll()
{
AppendLog("Calling Accept All");
await _cmpManager.AcceptAll();
AppendLog("Accepted All");
}
private void OnClickRequestATTrackingStatus()
{
AppendLog("Request AT Tracking Status");
_ = _cmpManager.RequestATTrackingPermission();
}
private async void OnclickCheckConsentRequired()
{
AppendLog("Calling Check Consent Required");
var isRequired = await _cmpManager.CheckConsentIsRequired();
AppendLog($"Is consent required: {isRequired}");
}
private void OnClickOpenConsentLayer()
{
AppendLog("Open Consent Layer");
_cmpManager.OpenConsentLayer();
}
private void OnClickOpenConsentLayerOnCheck()
{
AppendLog("Open Consent Layer on Check");
_cmpManager.OpenConsentLayerOnCheck();
}
private void OnClickImportCmpString()
{
AppendLog("Click Import");
ImportCmpString();
}
private void OnClickResetConsentData()
{
_cmpManager.Reset();
AppendLog("Reset");
}
private void OnClickDebugConsentStatus()
{
DebugConsentStatus();
}
private void OnClickExportCmpString()
{
var cmpString = _cmpManager.ExportCmpString();
AppendLog($"Exported CMP String: {cmpString}");
}
private void CheckHasPurpose(string purposeId)
{
var hasPurpose = _cmpManager.HasPurpose(purposeId);
AppendLog($"Has Purpose ({purposeId}): {hasPurpose}");
}
private void CheckHasVendor(string vendorId)
{
var hasVendor = _cmpManager.HasVendor(vendorId);
AppendLog($"Has Vendor ({vendorId}): {hasVendor}");
}
private async void ImportCmpString()
{
var cmpString = _importStringInputField;
CmpImportResult result;
if (!string.IsNullOrEmpty(cmpString))
{
AppendLog($"Importing CMP String from input field: {cmpString}");
result = await _cmpManager.ImportCmpString(cmpString);
}
else
{
AppendLog($"Importing CMP String from sample string: {cmpString}");
result = await _cmpManager.ImportCmpString(cmpString);
}
AppendLog($"Unity import result: {result.IsSuccess} with message: {result.Message}");
}
#endregion
#region Callbacks
public void OnGoogleConsentUpdated(CmpGoogleConsentModeStatus status)
{
// Convert CmpGoogleConsentModeStatus to Firebase compatible dictionary
var firebaseConsentDict = new Dictionary<ConsentType, ConsentStatus>();
foreach (var consent in status.ConsentDictionary)
{
// Convert GoogleConsentType to Firebase ConsentType
var firebaseConsentType = ConvertToFirebaseConsentType(consent.Key);
// Convert GoogleConsentStatus to Firebase ConsentStatus
var firebaseConsentStatus = ConvertToFirebaseConsentStatus(consent.Value);
firebaseConsentDict[firebaseConsentType] = firebaseConsentStatus;
}
// Apply the consent settings to Firebase Analytics
FirebaseAnalytics.SetConsent(firebaseConsentDict);
AppendLog($"Google Consent Mode: {firebaseConsentDict}");
}
private static ConsentType ConvertToFirebaseConsentType(GoogleConsentType googleConsentType)
{
return googleConsentType switch
{
GoogleConsentType.AnalyticsStorage => ConsentType.AnalyticsStorage,
GoogleConsentType.AdStorage => ConsentType.AdStorage,
GoogleConsentType.AdUserData => ConsentType.AdUserData,
GoogleConsentType.AdPersonalization => ConsentType.AdPersonalization,
_ => throw new InvalidEnumArgumentException($"Unknown GoogleConsentType: {googleConsentType}")
};
}
private static ConsentStatus ConvertToFirebaseConsentStatus(GoogleConsentStatus googleConsentStatus)
{
return googleConsentStatus switch
{
GoogleConsentStatus.Granted => ConsentStatus.Granted,
GoogleConsentStatus.Denied => ConsentStatus.Denied,
_ => throw new InvalidEnumArgumentException($"Unknown GoogleConsentStatus: {googleConsentStatus}")
};
}
public void OnClose()
{
LogThreadContext("OnClose");
AppendLog("CMPConsentTool closed");
}
public void OnCmpButtonClicked(CmpButtonEvent buttonEvent)
{
LogThreadContext("OnCmpButtonClicked");
AppendLog($"CMPButton clicked. Event: {buttonEvent}");
}
public void OnNotOpened()
{
LogThreadContext("OnNotOpened");
AppendLog("CMPConsentTool not opened");
}
public void OnError(CmpErrorType errorType, string message)
{
LogThreadContext("OnError");
AppendLog($"Error: {errorType}, {message}");
}
public void OnOpen()
{
LogThreadContext("OnOpen");
AppendLog("CMPConsentTool opened");
}
#if UNITY_IOS
private void OnCmpATTrackingStatusChanged(ATTrackingManagerAuthorizationStatus oldStatus,
ATTrackingManagerAuthorizationStatus newStatus, double lastUpdated)
{
var unixTime = lastUpdated;
var dtDateTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
dtDateTime = dtDateTime.AddSeconds(unixTime).ToLocalTime();
AppendLog("OnCmpATTrackingStatusChanged: " + newStatus + " lastUpdated: " + dtDateTime);
}
#endif
#endregion
private void DebugConsentStatus()
{
var hasConsent = _cmpManager.HasConsent();
var allPurposes = _cmpManager.GetAllPurposes();
var disabledPurposes = _cmpManager.GetAllPurposes();
var enabledPurposes = _cmpManager.GetEnabledPurposes();
var allVendors = _cmpManager.GetAllVendors();
var disabledVendors = _cmpManager.GetDisabledVendors();
var enabledVendors = _cmpManager.GetEnabledVendors();
var exportCmp = _cmpManager.ExportCmpString();
AppendLog("-----------------");
AppendLog($"Unity All Purposes: {string.Join(", ", allPurposes)}");
AppendLog($"Unity Disabled Purposes: {string.Join(", ", disabledPurposes)}");
AppendLog($"Unity Enabled Purposes: {string.Join(", ", enabledPurposes)}");
AppendLog($"Unity All Vendors: {string.Join(", ", allVendors)}");
AppendLog($"Unity Disabled Vendors: {string.Join(", ", disabledVendors)}");
AppendLog($"Unity Enabled Vendors: {string.Join(", ", enabledVendors)}");
AppendLog($"Unity Exported CMP String: {exportCmp}");
AppendLog($"Unity Has Consent: {hasConsent}");
AppendLog($"Unity US Privacy String: {_cmpManager.GetUsPrivacyString()}");
AppendLog($"Unity Google Ac String: {_cmpManager.GetGoogleAcString()}");
AppendLog($"Unity Has Purpose C1: {_cmpManager.HasPurpose("c1")}");
AppendLog($"Unity Has Vendor 10: {_cmpManager.HasVendor("628")}");
AppendLog($"Unity Google Consent Mode Status: {_cmpManager.GetGoogleConsentModeStatus()}");
AppendLog("-----------------");
}
#region Helper
private void LogThreadContext(string callbackName)
{
var onMainThread = IsMainThread();
var threadId = Thread.CurrentThread.ManagedThreadId;
AppendLog($"{callbackName} called. Is main thread: {onMainThread} ID: {threadId}");
}
private bool IsMainThread()
{
return _mainThread.Equals(Thread.CurrentThread);
}
private void AppendLog(string message)
{
Debug.Log(message);
if (uiManager != null)
{
uiManager.AddLogText(message);
}
}
private List<string> GetScreenConfigOptions()
{
var options = new List<string>();
foreach (var config in Enum.GetValues(typeof(CmpUIConfig.ScreenConfig)))
{
options.Add(config.ToString());
}
return options;
}
#endregion
}
}