Compliant with GDPR, CCPA, COPPA, LGPD, PECR, PDPA, PIPEDA, and more.
UniConsent CMP 是用于在 Android 应用中处理 GDPR IAB TCF 2.3 同意管理的软件包。您可以在 "demo" 目录中找到集成了 UniConsent CMP 的演示应用。
将 UniConsentSDK-release.aar 添加到项目的 libs/ 目录中,并更新 build.gradle:
implementation files('libs/UniConsentSDK-release.aar')
implementation 'androidx.appcompat:appcompat:1.7.1'
implementation 'com.google.android.material:material:1.13.0'
implementation 'com.iabtcf:iabtcf-core:2.0.10'
implementation 'com.iabtcf:iabtcf-decoder:2.0.10'
在集成 SDK 之前,请在 UniConsent 控制台 中自定义同意横幅的外观,以匹配您的品牌并优化同意率。
前往 Projects → 选择您的项目 → Settings → Step 5: UI & Style Settings 进行配置:
如需更精确的控制,请在 Step 5 的 CSS Content 字段中添加自定义 CSS。建议使用此功能让横幅呈现原生应用的观感,以获得最佳同意率:
/* Example: Style the accept button to match your brand */
.unic-btn-accept {
background-color: #4CAF50;
border-radius: 8px;
font-weight: 600;
}
/* Example: Adjust banner font */
.unic-banner {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}
/* Example: Make the reject button less prominent */
.unic-btn-reject {
background-color: transparent;
border: 1px solid #ccc;
color: #666;
}
提示: 具有原生应用观感的精心设计的同意界面通常能获得更高的同意率。用户更可能对符合预期外观和风格的横幅做出积极响应。
要在应用中使用 UniConsent CMP,请按照以下步骤操作:
使用客户经理提供的 App ID 初始化 CMP:
UniConsent UniConsentCMP = UniConsent.getInstance();
UniConsentCMP.setAppId("YOUR_APP_ID");
显示 CMP 界面:
// Display CMP as full-screen page (default)
UniConsent.getInstance().launchCMP();
// Display CMP as a modal bottom sheet
UniConsent.getInstance().launchCMP(CMPDisplayMode.BOTTOM_SHEET);
// Display CMP as a center dialog
UniConsent.getInstance().launchCMP(CMPDisplayMode.DIALOG);
您还可以使用指定的阶段和显示模式启动:
// Launch at a specific stage with a display mode
UniConsent.getInstance().launchCMP(Stage.GDPRFirstScreen, CMPDisplayMode.BOTTOM_SHEET);
// Bottom sheet at 70% of the screen height
UniConsent.getInstance().launchCMP(Stage.GDPRFirstScreen, CMPDisplayMode.BOTTOM_SHEET, 0.7);
// Center dialog at 80% width and 60% height
UniConsent.getInstance().launchCMP(Stage.GDPRFirstScreen, CMPDisplayMode.DIALOG, 0.8, 0.6);
// Or set a default display mode for all launches
UniConsent.getInstance().setDisplayMode(CMPDisplayMode.BOTTOM_SHEET);
UniConsent.getInstance().launchCMP();
自 SDK 26.8.0 版本起可用。
两个比例参数均为可选,所有显示模式不传它们也能正常工作。仅在需要覆盖默认尺寸时才传入比例(相对屏幕的比例,需大于 0 且不超过 1);无效值会抛出 IllegalArgumentException:
| 参数 | 适用于 | 省略时的默认值 |
|---|---|---|
heightRatio | BOTTOM_SHEET、DIALOG | 面板:屏幕的 90%。对话框:70% |
widthRatio | 仅 DIALOG | 屏幕宽度的 90%,最大 500dp |
不适用于所选显示模式的比例会被忽略(FULL_SCREEN 会忽略两者)。
当 vendorList 更新时自动检查同意是否过期:
// Check if consent should be requested (e.g. first visit, or vendor list updated)
if (UniConsentCMP.shouldRequestConsent()) {
UniConsentCMP.launchCMP();
}
获取 tcString(如需要):
// Get tcString
UniConsent.getInstance().getTCString();
读取同意状态:
// Read consent status
UniConsent.getInstance().hasIABPurposeConsent(1);
UniConsent.getInstance().hasIABVendorConsent(1);
重置同意状态(如需要):
// Reset consent status
UniConsent.getInstance().clearData();
自 SDK 26.7.0 版本起可用。如果您构建自己的首层同意界面而不显示 CMP webview,可以直接保存同意状态:
// Grant consent for all purposes, special features and vendors
boolean saved = UniConsent.getInstance().agreeAll();
// Reject all purposes, special features and vendors
boolean saved = UniConsent.getInstance().rejectAll();
这两个 API 保存与 CMP UI 流程相同的同意值(IAB TCF 键、附加同意和同意模式信号),并向订阅者发送相同的关闭事件。
注意: 请仅在
init()完成后(例如 READY 事件之后)调用这些 API。它们依赖初始化期间获取的供应商列表和项目配置,如果无法保存同意状态则返回false。重要: 您的自定义首层界面仍须满足 IAB TCF 要求(披露目的和供应商,并以同等显著程度提供接受和拒绝选项)。用户仍应能够打开完整的 CMP 界面(
launchCMP())进行精细选择。
如果您的应用打开了加载带有 UniConsent CMP 标签的网页的 WebView,您可以传递原生同意状态,这样 CMP 标签会识别已有的同意记录,不会再次显示横幅。
在页面开始加载时、CMP 标签运行之前注入同意状态:
WebView webView = findViewById(R.id.webView);
webView.getSettings().setJavaScriptEnabled(true);
webView.setWebViewClient(new WebViewClient() {
@Override
public void onPageStarted(WebView view, String url, android.graphics.Bitmap favicon) {
UniConsent.getInstance().syncConsent(view);
}
});
webView.loadUrl("https://example.com");
import com.uniconsent.sdk.CMPDisplayMode;
import com.uniconsent.sdk.Event;
import com.uniconsent.sdk.EventHandler;
import com.uniconsent.sdk.Stage;
import com.uniconsent.sdk.UniConsent;
public class MainActivity extends AppCompatActivity implements EventHandler {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
UniConsent UniConsentCMP = UniConsent.getInstance();
UniConsentCMP.setAppId("YOUR_APP_ID");
UniConsentCMP.init(this);
// register callback events
UniConsentCMP.subscribe(this);
// check if consent should be requested
if (UniConsentCMP.shouldRequestConsent()) {
UniConsentCMP.launchCMP();
}
}
public void openUniConsentUI(View view) {
// Full screen
UniConsent.getInstance().launchCMP(Stage.GDPRFirstScreen);
}
public void openAsBottomSheet(View view) {
// Bottom sheet
UniConsent.getInstance().launchCMP(Stage.GDPRFirstScreen, CMPDisplayMode.BOTTOM_SHEET);
}
public void openAsDialog(View view) {
// Center dialog
UniConsent.getInstance().launchCMP(Stage.GDPRFirstScreen, CMPDisplayMode.DIALOG);
}
@Override
public void handle(Event event) {
UniConsent.getInstance().hasIABVendorConsent(1);
}
}
package com.example.myapplication
import android.os.Bundle
import android.util.Log
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.example.myapplication.ui.theme.MyApplicationTheme
import com.uniconsent.sdk.CMPDisplayMode
import com.uniconsent.sdk.Event
import com.uniconsent.sdk.EventHandler
import com.uniconsent.sdk.Stage
import com.uniconsent.sdk.UniConsent
class MainActivity : ComponentActivity(), EventHandler {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val cmp = UniConsent.getInstance()
cmp.appId = "YOUR_APP_ID"
cmp.init(this)
cmp.subscribe(this)
if (cmp.shouldRequestConsent()) {
cmp.launchCMP();
}
setContent {
MyApplicationTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Button(onClick = {
UniConsent.getInstance().launchCMP(Stage.GDPRFirstScreen)
}) {
Text("Privacy Settings")
}
Spacer(modifier = Modifier.height(16.dp))
Button(onClick = {
UniConsent.getInstance().launchCMP(
Stage.GDPRFirstScreen, CMPDisplayMode.BOTTOM_SHEET
)
}) {
Text("Privacy Settings (Bottom Sheet)")
}
Spacer(modifier = Modifier.height(16.dp))
Button(onClick = {
UniConsent.getInstance().launchCMP(
Stage.GDPRFirstScreen, CMPDisplayMode.DIALOG
)
}) {
Text("Privacy Settings (Dialog)")
}
}
}
}
}
}
override fun handle(event: Event?) {
Log.d("CMP_EVENT", event.toString())
UniConsent.getInstance().hasIABVendorConsent(1)
}
}
<uses-permission android:name="android.permission.INTERNET" />
设置默认同意状态键值对:
<meta-data android:name="firebase_analytics_collection_enabled" android:value="false" />
<meta-data android:name="google_analytics_default_allow_analytics_storage" android:value="false" />
<meta-data android:name="google_analytics_default_allow_ad_storage" android:value="false" />
<meta-data android:name="google_analytics_default_allow_ad_user_data" android:value="false" />
<meta-data android:name="google_analytics_default_allow_ad_personalization_signals" android:value="false" />
根据同意标志控制分析:
// <= 25.6.1
override fun handle(event: Event?) {
Log.d("CMP_EVENT", event.toString())
if(UniConsent.getInstance().hasIABPurposeConsent(1)) {
// Set consent types.
Map<FirebaseAnalytics.ConsentType, FirebaseAnalytics.ConsentStatus> consentMap = new EnumMap<>(FirebaseAnalytics.ConsentType.class);
consentMap.put(FirebaseAnalytics.ConsentType.ANALYTICS_STORAGE, FirebaseAnalytics.ConsentStatus.GRANTED);
consentMap.put(FirebaseAnalytics.ConsentType.AD_STORAGE, FirebaseAnalytics.ConsentStatus.GRANTED);
consentMap.put(FirebaseAnalytics.ConsentType.AD_USER_DATA, FirebaseAnalytics.ConsentStatus.GRANTED);
consentMap.put(FirebaseAnalytics.ConsentType.AD_PERSONALIZATION, FirebaseAnalytics.ConsentStatus.GRANTED);
mFirebaseAnalytics.setConsent(consentMap);
mFirebaseAnalytics.setAnalyticsCollectionEnabled(true);
} else {
// Set consent types.
Map<FirebaseAnalytics.ConsentType, FirebaseAnalytics.ConsentStatus> consentMap = new EnumMap<>(FirebaseAnalytics.ConsentType.class);
consentMap.put(FirebaseAnalytics.ConsentType.ANALYTICS_STORAGE, FirebaseAnalytics.ConsentStatus.DENIED);
consentMap.put(FirebaseAnalytics.ConsentType.AD_STORAGE, FirebaseAnalytics.ConsentStatus.DENIED);
consentMap.put(FirebaseAnalytics.ConsentType.AD_USER_DATA, FirebaseAnalytics.ConsentStatus.DENIED);
consentMap.put(FirebaseAnalytics.ConsentType.AD_PERSONALIZATION, FirebaseAnalytics.ConsentStatus.DENIED);
mFirebaseAnalytics.setConsent(consentMap);
mFirebaseAnalytics.setAnalyticsCollectionEnabled(false);
}
// other logics such as send analytics events
}
// > 25.6.1
override fun handle(event: Event?) {
Log.d("CMP_EVENT", event.toString())
// other logics such as send analytics events
// Example: send Firebase event or other analytics metrics
Bundle bundle = new Bundle();
bundle.putString(FirebaseAnalytics.Param.ITEM_ID, "id");
bundle.putString(FirebaseAnalytics.Param.ITEM_NAME, "name");
bundle.putString(FirebaseAnalytics.Param.CONTENT_TYPE, "image");
mFirebaseAnalytics.logEvent(FirebaseAnalytics.Event.SELECT_CONTENT, bundle);
}
更多信息请参阅 为应用设置 Consent Mode
SDK 支持三种同意界面显示模式,与 Flutter SDK API 一致:
| 模式 | 枚举值 | 说明 |
|---|---|---|
| 全屏 | CMPDisplayMode.FULL_SCREEN | 在新的全屏 Activity 中打开 CMP(默认) |
| 底部弹出 | CMPDisplayMode.BOTTOM_SHEET | 从底部滑出的模态弹窗(85% 高度) |
| 对话框 | CMPDisplayMode.DIALOG | 居中显示的对话框(90% 宽度,75% 高度) |
注意: 底部弹出和对话框模式要求您的 Activity 继承
AppCompatActivity或FragmentActivity。