Admob用户消息平台的强制同意

22
我从已弃用的GDPR Consent Library切换到了新的User Messaging Platform,并使用了 documentation 中所述的代码。
我注意到当用户点击管理选项然后点击确认选择时,广告将完全停止显示(Ad failed to load,no ad config),我找不到任何方法来检查用户是否同意使用个人数据。
这很麻烦,因为我的应用程序纯粹依赖于广告,如果广告不显示,我将损失金钱,因此我希望用户强制同意使用他们的个人数据,否则应用程序将无法使用。
我在Github上创建了一个测试项目,这样每个人都可以测试这种行为。如果您没有使用仿真器,则需要将“TEST_DEVICE_ID”更改为您自己的ID。
我该如何实现这一点?
2个回答

28
UMP将其输出写入SharedPreferences中的一些属性,详见此处。您可以编写一些辅助方法来查询这些属性,以了解用户授予的广告同意级别或用户是否属于欧洲经济区(EEA),但您需要查看的不仅仅是VendorConsents字符串。
通常有5个属性您需要查找,以确定是否会提供广告服务:
  • IABTCF_gdprApplies - 一个整数(0或1),表示用户是否在欧洲经济区
  • 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的文档这里,关于UMP资金选择表单与广告服务相关的实际结果实际上只有几种:
1. 用户点击了“同意全部” - 上述字符串将全部为1,并且将显示个性化广告。 2. 用户点击了“不同意任何内容” - 将不显示任何广告。 3. 用户点击了“管理”并选择了存储同意(目的1),并在非按字母顺序列出的供应商巨大列表中滚动选择了“Google” - 将显示非个性化广告。 4. 用户点击了“管理”并执行了比前一步骤更少的操作(例如,选择了存储和基本广告,但没有手动从供应商列表中选择Google) - 再次,将不显示任何广告。
这是一个相当不理想的选项集,因为#3几乎不可能发生,而#2和#4会导致用户在不支付费用的情况下获得无广告的应用程序。从实际角度来看,这已经移除了传统同意SDK中的“非个性化广告”选项(以及购买无广告应用程序的选项),并用完全禁用广告来替代。
我写了一些辅助方法,至少让您查询用户实际选择的内容并相应地采取行动。
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)
    }
}

注意:PreferenceManager.getDefaultSharedPreferences并没有被弃用,你只需要确保导入了androidx的包(import androidx.preference.PreferenceManager)。如果你导入了错误的包(import android.preference.PreferenceManager),它会被标记为已弃用。

全球供应商ID列表可以在此处找到:https://vendor-list.consensu.org/v2/vendor-list.json - Nicolas Prugne
谢谢,您也可以在此处访问它:https://iabeurope.eu/vendor-list-tcf-v2-0/ - Tyler V
似乎仍在使用传统内容SDK的应用现在只能获得非个性化的应用程序了!? - 3c71
这太棒了,感谢Tyler的工作。我注意到的另一件事是,在用户界面中,用户还可以选择关闭供应商的合法利益,这对我来说毫无意义。如果用户点击“管理”,然后选择存储同意(目的1),但关闭了谷歌广告的合法利益,将不会显示广告。为什么用户可以决定谷歌的合法利益是什么?完全荒谬。如果我选择 - undefined
供应商列表的链接似乎发生了变化。您可以在此处找到一个交互式列表,可以进行搜索:https://iabeurope.eu/vendor-list-tcf/ - undefined
@Frozax,谢谢你告诉我。我已经在答案中更新了链接。 - undefined

0

我找到了一个解决方法,但这不是最终的官方解决方案。

似乎如果用户同意个性化广告,在SharedPreferences中的一个字符串,其键为IABTCF_VendorConsents,将包含一些供应商对应的1和0。如果他没有同意,则此字符串将等于0

private val sp = PreferenceManager.getDefaultSharedPreferences(appContext)
fun consentedToPersonalizedAds() = sp.getString("IABTCF_VendorConsents", null) != "0"

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接