Skip to content

Commit 24afe63

Browse files
committed
Add GDPR and CCPA consent APIs to Chartboost Flutter Adapter Plugin
PiperOrigin-RevId: 702353803
1 parent 21463bf commit 24afe63

File tree

16 files changed

+893
-41
lines changed

16 files changed

+893
-41
lines changed

packages/mediation/gma_mediation_chartboost/android/build.gradle

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,19 +54,29 @@ android {
5454

5555
dependencies {
5656
implementation("com.google.ads.mediation:chartboost:9.8.1.0")
57-
testImplementation("org.jetbrains.kotlin:kotlin-test")
58-
testImplementation("org.mockito:mockito-core:5.0.0")
57+
testImplementation 'junit:junit:4.13.2'
58+
testImplementation 'androidx.test:core:1.6.1'
59+
testImplementation 'androidx.test:core-ktx:1.6.1'
60+
testImplementation 'androidx.test.ext:junit:1.2.1'
61+
testImplementation 'org.jetbrains.kotlin:kotlin-stdlib:2.0.21'
62+
testImplementation 'org.mockito:mockito-core:5.5.0'
63+
testImplementation 'org.mockito.kotlin:mockito-kotlin:5.1.0'
64+
testImplementation 'org.robolectric:robolectric:4.10.3'
5965
}
6066

6167
testOptions {
6268
unitTests.all {
63-
useJUnitPlatform()
69+
useJUnit()
6470

6571
testLogging {
66-
events "passed", "skipped", "failed", "standardOut", "standardError"
67-
outputs.upToDateWhen {false}
68-
showStandardStreams = true
72+
events "passed", "skipped", "failed", "standardOut", "standardError"
73+
outputs.upToDateWhen {false}
74+
showStandardStreams = true
6975
}
7076
}
77+
unitTests {
78+
includeAndroidResources = true
79+
unitTests.returnDefaultValues = true
80+
}
7181
}
7282
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
// Autogenerated from Pigeon (v22.6.2), do not edit directly.
2+
// See also: https://pub.dev/packages/pigeon
3+
@file:Suppress("UNCHECKED_CAST", "ArrayInDataClass")
4+
5+
package io.flutter.plugins.googlemobileads.mediation.gma_mediation_chartboost
6+
7+
import android.util.Log
8+
import io.flutter.plugin.common.BasicMessageChannel
9+
import io.flutter.plugin.common.BinaryMessenger
10+
import io.flutter.plugin.common.MessageCodec
11+
import io.flutter.plugin.common.StandardMessageCodec
12+
import java.io.ByteArrayOutputStream
13+
import java.nio.ByteBuffer
14+
15+
private fun wrapResult(result: Any?): List<Any?> {
16+
return listOf(result)
17+
}
18+
19+
private fun wrapError(exception: Throwable): List<Any?> {
20+
return if (exception is FlutterError) {
21+
listOf(
22+
exception.code,
23+
exception.message,
24+
exception.details
25+
)
26+
} else {
27+
listOf(
28+
exception.javaClass.simpleName,
29+
exception.toString(),
30+
"Cause: " + exception.cause + ", Stacktrace: " + Log.getStackTraceString(exception)
31+
)
32+
}
33+
}
34+
35+
/**
36+
* Error class for passing custom error details to Flutter via a thrown PlatformException.
37+
* @property code The error code.
38+
* @property message The error message.
39+
* @property details The error details. Must be a datatype supported by the api codec.
40+
*/
41+
class FlutterError (
42+
val code: String,
43+
override val message: String? = null,
44+
val details: Any? = null
45+
) : Throwable()
46+
47+
enum class ChartboostPrivacyStandard(val raw: Int) {
48+
GDPR(0),
49+
CCPA(1);
50+
51+
companion object {
52+
fun ofRaw(raw: Int): ChartboostPrivacyStandard? {
53+
return values().firstOrNull { it.raw == raw }
54+
}
55+
}
56+
}
57+
private open class ChartboostSDKApiPigeonCodec : StandardMessageCodec() {
58+
override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? {
59+
return when (type) {
60+
129.toByte() -> {
61+
return (readValue(buffer) as Long?)?.let {
62+
ChartboostPrivacyStandard.ofRaw(it.toInt())
63+
}
64+
}
65+
else -> super.readValueOfType(type, buffer)
66+
}
67+
}
68+
override fun writeValue(stream: ByteArrayOutputStream, value: Any?) {
69+
when (value) {
70+
is ChartboostPrivacyStandard -> {
71+
stream.write(129)
72+
writeValue(stream, value.raw)
73+
}
74+
else -> super.writeValue(stream, value)
75+
}
76+
}
77+
}
78+
79+
/**
80+
* The generated classes set the channels to call the methods in the
81+
* corresponding kotlin ChartboostSDKApi interface and swift ChartboostSDKApi
82+
* protocol from the dart layer.
83+
*
84+
* Generated interface from Pigeon that represents a handler of messages from Flutter.
85+
*/
86+
interface ChartboostSDKApi {
87+
/** Used to configure GDPR consent on the Android or iOS Chartboost SDK */
88+
fun setGDPRConsent(userConsent: Boolean)
89+
/** Used to opt out of the sale of personal information in Chartboost SDK. */
90+
fun setCCPAConsent(userOptIn: Boolean)
91+
/** Used to clear any of the privacy data use consent above. */
92+
fun clearDataUseConsent(privacyStandard: ChartboostPrivacyStandard)
93+
94+
companion object {
95+
/** The codec used by ChartboostSDKApi. */
96+
val codec: MessageCodec<Any?> by lazy {
97+
ChartboostSDKApiPigeonCodec()
98+
}
99+
/** Sets up an instance of `ChartboostSDKApi` to handle messages through the `binaryMessenger`. */
100+
@JvmOverloads
101+
fun setUp(binaryMessenger: BinaryMessenger, api: ChartboostSDKApi?, messageChannelSuffix: String = "") {
102+
val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else ""
103+
run {
104+
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.gma_mediation_chartboost.ChartboostSDKApi.setGDPRConsent$separatedMessageChannelSuffix", codec)
105+
if (api != null) {
106+
channel.setMessageHandler { message, reply ->
107+
val args = message as List<Any?>
108+
val userConsentArg = args[0] as Boolean
109+
val wrapped: List<Any?> = try {
110+
api.setGDPRConsent(userConsentArg)
111+
listOf(null)
112+
} catch (exception: Throwable) {
113+
wrapError(exception)
114+
}
115+
reply.reply(wrapped)
116+
}
117+
} else {
118+
channel.setMessageHandler(null)
119+
}
120+
}
121+
run {
122+
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.gma_mediation_chartboost.ChartboostSDKApi.setCCPAConsent$separatedMessageChannelSuffix", codec)
123+
if (api != null) {
124+
channel.setMessageHandler { message, reply ->
125+
val args = message as List<Any?>
126+
val userOptInArg = args[0] as Boolean
127+
val wrapped: List<Any?> = try {
128+
api.setCCPAConsent(userOptInArg)
129+
listOf(null)
130+
} catch (exception: Throwable) {
131+
wrapError(exception)
132+
}
133+
reply.reply(wrapped)
134+
}
135+
} else {
136+
channel.setMessageHandler(null)
137+
}
138+
}
139+
run {
140+
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.gma_mediation_chartboost.ChartboostSDKApi.clearDataUseConsent$separatedMessageChannelSuffix", codec)
141+
if (api != null) {
142+
channel.setMessageHandler { message, reply ->
143+
val args = message as List<Any?>
144+
val privacyStandardArg = args[0] as ChartboostPrivacyStandard
145+
val wrapped: List<Any?> = try {
146+
api.clearDataUseConsent(privacyStandardArg)
147+
listOf(null)
148+
} catch (exception: Throwable) {
149+
wrapError(exception)
150+
}
151+
reply.reply(wrapped)
152+
}
153+
} else {
154+
channel.setMessageHandler(null)
155+
}
156+
}
157+
}
158+
}
159+
}

packages/mediation/gma_mediation_chartboost/android/src/main/kotlin/io/flutter/plugins/googlemobileads/mediation/gma_mediation_chartboost/GmaMediationChartboostPlugin.kt

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,55 @@
1414

1515
package io.flutter.plugins.googlemobileads.mediation.gma_mediation_chartboost
1616

17+
import android.content.Context
18+
import com.chartboost.sdk.Chartboost
19+
import com.chartboost.sdk.privacy.model.CCPA
20+
import com.chartboost.sdk.privacy.model.GDPR
1721
import io.flutter.embedding.engine.plugins.FlutterPlugin
22+
import io.flutter.embedding.engine.plugins.activity.ActivityAware
23+
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding
1824

1925
/** Required to link the Android dependency of the Chartboost Adapter. */
20-
class GmaMediationChartboostPlugin: FlutterPlugin {
21-
override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { }
26+
class GmaMediationChartboostPlugin : FlutterPlugin, ActivityAware, ChartboostSDKApi {
27+
private lateinit var context: Context
2228

23-
override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) { }
29+
override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
30+
context = flutterPluginBinding.applicationContext
31+
ChartboostSDKApi.setUp(flutterPluginBinding.binaryMessenger, this)
32+
}
33+
34+
override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
35+
ChartboostSDKApi.setUp(binding.binaryMessenger, null)
36+
}
37+
38+
override fun setGDPRConsent(userConsent: Boolean) {
39+
val dataUserConsent =
40+
if (userConsent) GDPR(GDPR.GDPR_CONSENT.BEHAVIORAL)
41+
else GDPR(GDPR.GDPR_CONSENT.NON_BEHAVIORAL)
42+
Chartboost.addDataUseConsent(context, dataUserConsent)
43+
}
44+
45+
override fun setCCPAConsent(userOptIn: Boolean) {
46+
val dataUseConsent =
47+
if (userOptIn) CCPA(CCPA.CCPA_CONSENT.OPT_IN_SALE) else CCPA(CCPA.CCPA_CONSENT.OPT_OUT_SALE)
48+
Chartboost.addDataUseConsent(context, dataUseConsent)
49+
}
50+
51+
override fun clearDataUseConsent(privacyStandard: ChartboostPrivacyStandard) {
52+
val dataUseConsent = when (privacyStandard) {
53+
ChartboostPrivacyStandard.GDPR ->
54+
GDPR(GDPR.GDPR_CONSENT.BEHAVIORAL)
55+
ChartboostPrivacyStandard.CCPA ->
56+
CCPA(CCPA.CCPA_CONSENT.OPT_IN_SALE)
57+
}
58+
Chartboost.clearDataUseConsent(context, dataUseConsent.privacyStandard)
59+
}
60+
61+
override fun onDetachedFromActivity() {}
62+
63+
override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {}
64+
65+
override fun onAttachedToActivity(binding: ActivityPluginBinding) {}
66+
67+
override fun onDetachedFromActivityForConfigChanges() {}
2468
}

packages/mediation/gma_mediation_chartboost/android/src/test/kotlin/io/flutter/plugins/googlemobileads/mediation/gma_mediation_chartboost/GmaMediationChartboostPluginTest.kt

Lines changed: 115 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,118 @@
1414

1515
package io.flutter.plugins.googlemobileads.mediation.gma_mediation_chartboost
1616

17-
internal class GmaMediationChartboostPluginTest {}
17+
import android.content.Context
18+
import androidx.test.core.app.ApplicationProvider
19+
import androidx.test.ext.junit.runners.AndroidJUnit4
20+
import com.chartboost.sdk.Chartboost
21+
import com.chartboost.sdk.privacy.model.CCPA
22+
import com.chartboost.sdk.privacy.model.GDPR
23+
import io.flutter.embedding.engine.plugins.FlutterPlugin
24+
import io.flutter.plugin.common.BinaryMessenger
25+
import org.junit.Test
26+
import org.junit.runner.RunWith
27+
import org.mockito.Mockito.mockStatic
28+
import org.mockito.kotlin.doReturn
29+
import org.mockito.kotlin.eq
30+
import org.mockito.kotlin.mock
31+
32+
@RunWith(AndroidJUnit4::class)
33+
internal class GmaMediationChartboostPluginTest {
34+
private val context = ApplicationProvider.getApplicationContext<Context>()
35+
private val mockBinaryMessenger = mock<BinaryMessenger>()
36+
private val mockFlutterPluginBinding =
37+
mock<FlutterPlugin.FlutterPluginBinding> {
38+
on { applicationContext } doReturn context
39+
on { binaryMessenger } doReturn mockBinaryMessenger
40+
}
41+
42+
@Test
43+
fun setGDPRConsent_withTrueValue_addsDataUserConsentAsBehavioral() {
44+
val plugin = GmaMediationChartboostPlugin()
45+
mockStatic(ChartboostSDKApi::class.java).use { mockedchartboostSDKApi ->
46+
plugin.onAttachedToEngine(mockFlutterPluginBinding)
47+
val expectedDataUserConsent = GDPR(GDPR.GDPR_CONSENT.BEHAVIORAL)
48+
49+
plugin.setGDPRConsent(true)
50+
51+
mockedchartboostSDKApi.verify {
52+
Chartboost.addDataUseConsent(eq(context), eq(expectedDataUserConsent))
53+
}
54+
}
55+
}
56+
57+
@Test
58+
fun setGDPRConsent_withFalseValue_addsDataUserConsentAsNonBehavioral() {
59+
val plugin = GmaMediationChartboostPlugin()
60+
mockStatic(ChartboostSDKApi::class.java).use { mockedchartboostSDKApi ->
61+
plugin.onAttachedToEngine(mockFlutterPluginBinding)
62+
val expectedDataUserConsent = GDPR(GDPR.GDPR_CONSENT.NON_BEHAVIORAL)
63+
64+
plugin.setGDPRConsent(false)
65+
66+
mockedchartboostSDKApi.verify {
67+
Chartboost.addDataUseConsent(eq(context), eq(expectedDataUserConsent))
68+
}
69+
}
70+
}
71+
72+
@Test
73+
fun setCCPAConsent_withTrueValue_addsDataUserConsentAsOptInSale() {
74+
val plugin = GmaMediationChartboostPlugin()
75+
mockStatic(ChartboostSDKApi::class.java).use { mockedchartboostSDKApi ->
76+
plugin.onAttachedToEngine(mockFlutterPluginBinding)
77+
val expectedDataUserConsent = CCPA(CCPA.CCPA_CONSENT.OPT_IN_SALE)
78+
79+
plugin.setCCPAConsent(true)
80+
81+
mockedchartboostSDKApi.verify {
82+
Chartboost.addDataUseConsent(eq(context), eq(expectedDataUserConsent))
83+
}
84+
}
85+
}
86+
87+
@Test
88+
fun setCCPAConsent_withFalseValue_addsDataUserConsentAsOptOutSale() {
89+
val plugin = GmaMediationChartboostPlugin()
90+
mockStatic(ChartboostSDKApi::class.java).use { mockedchartboostSDKApi ->
91+
plugin.onAttachedToEngine(mockFlutterPluginBinding)
92+
val expectedDataUserConsent = CCPA(CCPA.CCPA_CONSENT.OPT_OUT_SALE)
93+
94+
plugin.setCCPAConsent(false)
95+
96+
mockedchartboostSDKApi.verify {
97+
Chartboost.addDataUseConsent(eq(context), eq(expectedDataUserConsent))
98+
}
99+
}
100+
}
101+
102+
@Test
103+
fun clearDataUseConsent_usesGDPRPrivacyStandard() {
104+
val plugin = GmaMediationChartboostPlugin()
105+
mockStatic(ChartboostSDKApi::class.java).use { mockedchartboostSDKApi ->
106+
plugin.onAttachedToEngine(mockFlutterPluginBinding)
107+
val expectedPrivacyStandard = GDPR(GDPR.GDPR_CONSENT.NON_BEHAVIORAL).privacyStandard
108+
109+
plugin.clearDataUseConsent(ChartboostPrivacyStandard.GDPR)
110+
111+
mockedchartboostSDKApi.verify {
112+
Chartboost.clearDataUseConsent(eq(context), eq(expectedPrivacyStandard))
113+
}
114+
}
115+
}
116+
117+
@Test
118+
fun clearDataUseConsent_usesCCPAPrivacyStandard() {
119+
val plugin = GmaMediationChartboostPlugin()
120+
mockStatic(ChartboostSDKApi::class.java).use { mockedchartboostSDKApi ->
121+
plugin.onAttachedToEngine(mockFlutterPluginBinding)
122+
val expectedPrivacyStandard = CCPA(CCPA.CCPA_CONSENT.OPT_OUT_SALE).privacyStandard
123+
124+
plugin.clearDataUseConsent(ChartboostPrivacyStandard.CCPA)
125+
126+
mockedchartboostSDKApi.verify {
127+
Chartboost.clearDataUseConsent(eq(context), eq(expectedPrivacyStandard))
128+
}
129+
}
130+
}
131+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1+
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
12
#include "Generated.xcconfig"
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1+
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
12
#include "Generated.xcconfig"

0 commit comments

Comments
 (0)