Admob 用户消息平台的强制同意
Mandatory Consent for Admob User Messaging Platform
我从已弃用的 GDPR 同意库切换到新的用户消息传递平台,并使用 documentation 中所述的代码。
我注意到当用户点击管理选项然后确认选择时,广告将完全停止显示(广告加载失败,没有广告配置),而且我无法找到检查用户是否不同意使用个人数据的方法。
这是有问题的,因为我的应用完全依赖于广告,如果广告不出现我就会赔钱,所以我想强制要求用户同意使用他们的个人数据,否则该应用程序应该无法使用。
我在 Github 上做了一个测试项目,所以每个人都可以测试这个行为。如果您没有使用模拟器,那么您需要将“TEST_DEVICE_ID”更改为您的。
我怎样才能做到这一点?
我找到了解决方法,但这不是最终的官方解决方案。
似乎如果用户同意个性化广告,SharedPreferences
中的字符串,即 IABTCF_VendorConsents,将包含对应的 1 和 0对一些供应商(我认为)。如果他不同意,这个字符串将等于 0.
private val sp = PreferenceManager.getDefaultSharedPreferences(appContext)
fun consentedToPersonalizedAds() = sp.getString("IABTCF_VendorConsents", null) != "0"
UMP 将其输出写入 SharedPreferences
中的某些属性,概述 here。您可以编写一些辅助方法来查询这些属性,以了解用户给予的广告同意级别或用户是否为 EEA,但您需要查看的不仅仅是 VendorConsents
字符串。
您通常需要查找 5 个属性来确定是否投放广告:
IABTCF_gdprApplies
- 一个整数(0 或 1),表示用户是否在 EEA
IABTCF_PurposeConsents
- 一个由 0 和 1 组成的字符串,最多 10 个条目,表示用户是否同意 10 个不同的目的
IABTCF_PurposeLegitimateInterests
- 一个由 0 和 1 组成的字符串,最多 10 个条目,指示该应用是否具有 10 种不同用途的合法利益
IABTCF_VendorConsents
- 任意长度的 0 和 1 字符串,指示给定供应商是否已就上述目的获得同意。每个供应商都有一个 ID,指示他们在字符串中的位置。例如 Google 的 ID 是 755,因此如果 Google 已获得同意,则此字符串中的第 755 个字符将是“1”。
IABTCF_VendorLegitimateInterests
- 类似于供应商同意字符串,不同之处在于它指示供应商是否对先前指定的目的具有合法利益。
根据 Google 文档 here UMP Funding Choices 表格在服务广告方面实际上只有几个实际结果:
- 用户点击了“全部同意”- 上面的字符串将全为 1,将显示个性化广告
- 用户点击了“同意 None”- 根本不会显示任何广告
- 用户单击“管理”并select同意存储(目的 1),然后滚动浏览非字母顺序列出的供应商的巨型列表,也select“Google” - 将显示非个性化广告
- 用户点击了“管理”并做了比前一步少的任何操作(例如 selected 存储和基本广告但没有手动 select Google 从供应商列表) - 同样,将不会显示任何广告
这是一组非常不理想的选项,因为 #3 极不可能发生,而 #2 和 #4 导致用户无需付费即可获得无广告的应用程序。出于所有实际目的,这已经删除了旧版同意 SDK 中的“非个性化广告”选项(以及购买无广告应用程序的选项),并将其替换为完全禁用广告。
我已经编写了一些辅助方法,至少可以让您查询用户实际 select 编辑的内容并采取相应的行动。
fun isGDPR(): Boolean {
val prefs = PreferenceManager.getDefaultSharedPreferences(applicationContext)
val gdpr = prefs.getInt("IABTCF_gdprApplies", 0)
return gdpr == 1
}
fun canShowAds(): Boolean {
val prefs = PreferenceManager.getDefaultSharedPreferences(applicationContext)
//https://github.com/InteractiveAdvertisingBureau/GDPR-Transparency-and-Consent-Framework/blob/master/TCFv2/IAB%20Tech%20Lab%20-%20CMP%20API%20v2.md#in-app-details
//https://support.google.com/admob/answer/9760862?hl=en&ref_topic=9756841
val purposeConsent = prefs.getString("IABTCF_PurposeConsents", "") ?: ""
val vendorConsent = prefs.getString("IABTCF_VendorConsents","") ?: ""
val vendorLI = prefs.getString("IABTCF_VendorLegitimateInterests","") ?: ""
val purposeLI = prefs.getString("IABTCF_PurposeLegitimateInterests","") ?: ""
val googleId = 755
val hasGoogleVendorConsent = hasAttribute(vendorConsent, index=googleId)
val hasGoogleVendorLI = hasAttribute(vendorLI, index=googleId)
// Minimum required for at least non-personalized ads
return hasConsentFor(listOf(1), purposeConsent, hasGoogleVendorConsent)
&& hasConsentOrLegitimateInterestFor(listOf(2,7,9,10), purposeConsent, purposeLI, hasGoogleVendorConsent, hasGoogleVendorLI)
}
fun canShowPersonalizedAds(): Boolean {
val prefs = PreferenceManager.getDefaultSharedPreferences(applicationContext)
//https://github.com/InteractiveAdvertisingBureau/GDPR-Transparency-and-Consent-Framework/blob/master/TCFv2/IAB%20Tech%20Lab%20-%20CMP%20API%20v2.md#in-app-details
//https://support.google.com/admob/answer/9760862?hl=en&ref_topic=9756841
val purposeConsent = prefs.getString("IABTCF_PurposeConsents", "") ?: ""
val vendorConsent = prefs.getString("IABTCF_VendorConsents","") ?: ""
val vendorLI = prefs.getString("IABTCF_VendorLegitimateInterests","") ?: ""
val purposeLI = prefs.getString("IABTCF_PurposeLegitimateInterests","") ?: ""
val googleId = 755
val hasGoogleVendorConsent = hasAttribute(vendorConsent, index=googleId)
val hasGoogleVendorLI = hasAttribute(vendorLI, index=googleId)
return hasConsentFor(listOf(1,3,4), purposeConsent, hasGoogleVendorConsent)
&& hasConsentOrLegitimateInterestFor(listOf(2,7,9,10), purposeConsent, purposeLI, hasGoogleVendorConsent, hasGoogleVendorLI)
}
// Check if a binary string has a "1" at position "index" (1-based)
private fun hasAttribute(input: String, index: Int): Boolean {
return input.length >= index && input[index-1] == '1'
}
// Check if consent is given for a list of purposes
private fun hasConsentFor(purposes: List<Int>, purposeConsent: String, hasVendorConsent: Boolean): Boolean {
return purposes.all { p -> hasAttribute(purposeConsent, p)} && hasVendorConsent
}
// Check if a vendor either has consent or legitimate interest for a list of purposes
private fun hasConsentOrLegitimateInterestFor(purposes: List<Int>, purposeConsent: String, purposeLI: String, hasVendorConsent: Boolean, hasVendorLI: Boolean): Boolean {
return purposes.all { p ->
(hasAttribute(purposeLI, p) && hasVendorLI) ||
(hasAttribute(purposeConsent, p) && hasVendorConsent)
}
}
我从已弃用的 GDPR 同意库切换到新的用户消息传递平台,并使用 documentation 中所述的代码。
我注意到当用户点击管理选项然后确认选择时,广告将完全停止显示(广告加载失败,没有广告配置),而且我无法找到检查用户是否不同意使用个人数据的方法。
这是有问题的,因为我的应用完全依赖于广告,如果广告不出现我就会赔钱,所以我想强制要求用户同意使用他们的个人数据,否则该应用程序应该无法使用。
我在 Github 上做了一个测试项目,所以每个人都可以测试这个行为。如果您没有使用模拟器,那么您需要将“TEST_DEVICE_ID”更改为您的。
我怎样才能做到这一点?
我找到了解决方法,但这不是最终的官方解决方案。
似乎如果用户同意个性化广告,SharedPreferences
中的字符串,即 IABTCF_VendorConsents,将包含对应的 1 和 0对一些供应商(我认为)。如果他不同意,这个字符串将等于 0.
private val sp = PreferenceManager.getDefaultSharedPreferences(appContext)
fun consentedToPersonalizedAds() = sp.getString("IABTCF_VendorConsents", null) != "0"
UMP 将其输出写入 SharedPreferences
中的某些属性,概述 here。您可以编写一些辅助方法来查询这些属性,以了解用户给予的广告同意级别或用户是否为 EEA,但您需要查看的不仅仅是 VendorConsents
字符串。
您通常需要查找 5 个属性来确定是否投放广告:
IABTCF_gdprApplies
- 一个整数(0 或 1),表示用户是否在 EEAIABTCF_PurposeConsents
- 一个由 0 和 1 组成的字符串,最多 10 个条目,表示用户是否同意 10 个不同的目的IABTCF_PurposeLegitimateInterests
- 一个由 0 和 1 组成的字符串,最多 10 个条目,指示该应用是否具有 10 种不同用途的合法利益IABTCF_VendorConsents
- 任意长度的 0 和 1 字符串,指示给定供应商是否已就上述目的获得同意。每个供应商都有一个 ID,指示他们在字符串中的位置。例如 Google 的 ID 是 755,因此如果 Google 已获得同意,则此字符串中的第 755 个字符将是“1”。IABTCF_VendorLegitimateInterests
- 类似于供应商同意字符串,不同之处在于它指示供应商是否对先前指定的目的具有合法利益。
根据 Google 文档 here UMP Funding Choices 表格在服务广告方面实际上只有几个实际结果:
- 用户点击了“全部同意”- 上面的字符串将全为 1,将显示个性化广告
- 用户点击了“同意 None”- 根本不会显示任何广告
- 用户单击“管理”并select同意存储(目的 1),然后滚动浏览非字母顺序列出的供应商的巨型列表,也select“Google” - 将显示非个性化广告
- 用户点击了“管理”并做了比前一步少的任何操作(例如 selected 存储和基本广告但没有手动 select Google 从供应商列表) - 同样,将不会显示任何广告
这是一组非常不理想的选项,因为 #3 极不可能发生,而 #2 和 #4 导致用户无需付费即可获得无广告的应用程序。出于所有实际目的,这已经删除了旧版同意 SDK 中的“非个性化广告”选项(以及购买无广告应用程序的选项),并将其替换为完全禁用广告。
我已经编写了一些辅助方法,至少可以让您查询用户实际 select 编辑的内容并采取相应的行动。
fun isGDPR(): Boolean {
val prefs = PreferenceManager.getDefaultSharedPreferences(applicationContext)
val gdpr = prefs.getInt("IABTCF_gdprApplies", 0)
return gdpr == 1
}
fun canShowAds(): Boolean {
val prefs = PreferenceManager.getDefaultSharedPreferences(applicationContext)
//https://github.com/InteractiveAdvertisingBureau/GDPR-Transparency-and-Consent-Framework/blob/master/TCFv2/IAB%20Tech%20Lab%20-%20CMP%20API%20v2.md#in-app-details
//https://support.google.com/admob/answer/9760862?hl=en&ref_topic=9756841
val purposeConsent = prefs.getString("IABTCF_PurposeConsents", "") ?: ""
val vendorConsent = prefs.getString("IABTCF_VendorConsents","") ?: ""
val vendorLI = prefs.getString("IABTCF_VendorLegitimateInterests","") ?: ""
val purposeLI = prefs.getString("IABTCF_PurposeLegitimateInterests","") ?: ""
val googleId = 755
val hasGoogleVendorConsent = hasAttribute(vendorConsent, index=googleId)
val hasGoogleVendorLI = hasAttribute(vendorLI, index=googleId)
// Minimum required for at least non-personalized ads
return hasConsentFor(listOf(1), purposeConsent, hasGoogleVendorConsent)
&& hasConsentOrLegitimateInterestFor(listOf(2,7,9,10), purposeConsent, purposeLI, hasGoogleVendorConsent, hasGoogleVendorLI)
}
fun canShowPersonalizedAds(): Boolean {
val prefs = PreferenceManager.getDefaultSharedPreferences(applicationContext)
//https://github.com/InteractiveAdvertisingBureau/GDPR-Transparency-and-Consent-Framework/blob/master/TCFv2/IAB%20Tech%20Lab%20-%20CMP%20API%20v2.md#in-app-details
//https://support.google.com/admob/answer/9760862?hl=en&ref_topic=9756841
val purposeConsent = prefs.getString("IABTCF_PurposeConsents", "") ?: ""
val vendorConsent = prefs.getString("IABTCF_VendorConsents","") ?: ""
val vendorLI = prefs.getString("IABTCF_VendorLegitimateInterests","") ?: ""
val purposeLI = prefs.getString("IABTCF_PurposeLegitimateInterests","") ?: ""
val googleId = 755
val hasGoogleVendorConsent = hasAttribute(vendorConsent, index=googleId)
val hasGoogleVendorLI = hasAttribute(vendorLI, index=googleId)
return hasConsentFor(listOf(1,3,4), purposeConsent, hasGoogleVendorConsent)
&& hasConsentOrLegitimateInterestFor(listOf(2,7,9,10), purposeConsent, purposeLI, hasGoogleVendorConsent, hasGoogleVendorLI)
}
// Check if a binary string has a "1" at position "index" (1-based)
private fun hasAttribute(input: String, index: Int): Boolean {
return input.length >= index && input[index-1] == '1'
}
// Check if consent is given for a list of purposes
private fun hasConsentFor(purposes: List<Int>, purposeConsent: String, hasVendorConsent: Boolean): Boolean {
return purposes.all { p -> hasAttribute(purposeConsent, p)} && hasVendorConsent
}
// Check if a vendor either has consent or legitimate interest for a list of purposes
private fun hasConsentOrLegitimateInterestFor(purposes: List<Int>, purposeConsent: String, purposeLI: String, hasVendorConsent: Boolean, hasVendorLI: Boolean): Boolean {
return purposes.all { p ->
(hasAttribute(purposeLI, p) && hasVendorLI) ||
(hasAttribute(purposeConsent, p) && hasVendorConsent)
}
}