From 24afe632fe9a3fdf45b72c36e182c08bbc720213 Mon Sep 17 00:00:00 2001 From: Aldo Becerril Date: Tue, 3 Dec 2024 08:42:12 -0800 Subject: [PATCH] Add GDPR and CCPA consent APIs to Chartboost Flutter Adapter Plugin PiperOrigin-RevId: 702353803 --- .../android/build.gradle | 22 ++- .../ChartboostSDKApi.g.kt | 159 +++++++++++++++ .../GmaMediationChartboostPlugin.kt | 50 ++++- .../GmaMediationChartboostPluginTest.kt | 116 ++++++++++- .../example/ios/Flutter/Debug.xcconfig | 1 + .../example/ios/Flutter/Release.xcconfig | 1 + .../example/ios/Podfile | 44 +++++ .../ios/Runner.xcodeproj/project.pbxproj | 22 ++- .../contents.xcworkspacedata | 3 + .../GmaMediationChartboostPluginTest.swift | 20 -- .../GmaMediationChartboostPluginTests.swift | 63 ++++++ .../ios/Classes/ChartboostSDKApi.g.swift | 181 ++++++++++++++++++ .../GmaMediationChartboostPlugin.swift | 70 ++++++- .../lib/chartboost_sdk_api.g.dart | 143 ++++++++++++++ .../lib/gma_mediation_chartboost.dart | 19 +- .../pigeons/chartboost_sdk_api.dart | 20 +- 16 files changed, 893 insertions(+), 41 deletions(-) create mode 100644 packages/mediation/gma_mediation_chartboost/android/src/main/kotlin/io/flutter/plugins/googlemobileads/mediation/gma_mediation_chartboost/ChartboostSDKApi.g.kt create mode 100644 packages/mediation/gma_mediation_chartboost/example/ios/Podfile delete mode 100644 packages/mediation/gma_mediation_chartboost/example/ios/RunnerTests/GmaMediationChartboostPluginTest.swift create mode 100644 packages/mediation/gma_mediation_chartboost/example/ios/RunnerTests/GmaMediationChartboostPluginTests.swift create mode 100644 packages/mediation/gma_mediation_chartboost/ios/Classes/ChartboostSDKApi.g.swift create mode 100644 packages/mediation/gma_mediation_chartboost/lib/chartboost_sdk_api.g.dart diff --git a/packages/mediation/gma_mediation_chartboost/android/build.gradle b/packages/mediation/gma_mediation_chartboost/android/build.gradle index 550e3b429..ea07b830a 100644 --- a/packages/mediation/gma_mediation_chartboost/android/build.gradle +++ b/packages/mediation/gma_mediation_chartboost/android/build.gradle @@ -54,19 +54,29 @@ android { dependencies { implementation("com.google.ads.mediation:chartboost:9.8.1.0") - testImplementation("org.jetbrains.kotlin:kotlin-test") - testImplementation("org.mockito:mockito-core:5.0.0") + testImplementation 'junit:junit:4.13.2' + testImplementation 'androidx.test:core:1.6.1' + testImplementation 'androidx.test:core-ktx:1.6.1' + testImplementation 'androidx.test.ext:junit:1.2.1' + testImplementation 'org.jetbrains.kotlin:kotlin-stdlib:2.0.21' + testImplementation 'org.mockito:mockito-core:5.5.0' + testImplementation 'org.mockito.kotlin:mockito-kotlin:5.1.0' + testImplementation 'org.robolectric:robolectric:4.10.3' } testOptions { unitTests.all { - useJUnitPlatform() + useJUnit() testLogging { - events "passed", "skipped", "failed", "standardOut", "standardError" - outputs.upToDateWhen {false} - showStandardStreams = true + events "passed", "skipped", "failed", "standardOut", "standardError" + outputs.upToDateWhen {false} + showStandardStreams = true } } + unitTests { + includeAndroidResources = true + unitTests.returnDefaultValues = true + } } } diff --git a/packages/mediation/gma_mediation_chartboost/android/src/main/kotlin/io/flutter/plugins/googlemobileads/mediation/gma_mediation_chartboost/ChartboostSDKApi.g.kt b/packages/mediation/gma_mediation_chartboost/android/src/main/kotlin/io/flutter/plugins/googlemobileads/mediation/gma_mediation_chartboost/ChartboostSDKApi.g.kt new file mode 100644 index 000000000..063a46492 --- /dev/null +++ b/packages/mediation/gma_mediation_chartboost/android/src/main/kotlin/io/flutter/plugins/googlemobileads/mediation/gma_mediation_chartboost/ChartboostSDKApi.g.kt @@ -0,0 +1,159 @@ +// Autogenerated from Pigeon (v22.6.2), do not edit directly. +// See also: https://pub.dev/packages/pigeon +@file:Suppress("UNCHECKED_CAST", "ArrayInDataClass") + +package io.flutter.plugins.googlemobileads.mediation.gma_mediation_chartboost + +import android.util.Log +import io.flutter.plugin.common.BasicMessageChannel +import io.flutter.plugin.common.BinaryMessenger +import io.flutter.plugin.common.MessageCodec +import io.flutter.plugin.common.StandardMessageCodec +import java.io.ByteArrayOutputStream +import java.nio.ByteBuffer + +private fun wrapResult(result: Any?): List { + return listOf(result) +} + +private fun wrapError(exception: Throwable): List { + return if (exception is FlutterError) { + listOf( + exception.code, + exception.message, + exception.details + ) + } else { + listOf( + exception.javaClass.simpleName, + exception.toString(), + "Cause: " + exception.cause + ", Stacktrace: " + Log.getStackTraceString(exception) + ) + } +} + +/** + * Error class for passing custom error details to Flutter via a thrown PlatformException. + * @property code The error code. + * @property message The error message. + * @property details The error details. Must be a datatype supported by the api codec. + */ +class FlutterError ( + val code: String, + override val message: String? = null, + val details: Any? = null +) : Throwable() + +enum class ChartboostPrivacyStandard(val raw: Int) { + GDPR(0), + CCPA(1); + + companion object { + fun ofRaw(raw: Int): ChartboostPrivacyStandard? { + return values().firstOrNull { it.raw == raw } + } + } +} +private open class ChartboostSDKApiPigeonCodec : StandardMessageCodec() { + override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? { + return when (type) { + 129.toByte() -> { + return (readValue(buffer) as Long?)?.let { + ChartboostPrivacyStandard.ofRaw(it.toInt()) + } + } + else -> super.readValueOfType(type, buffer) + } + } + override fun writeValue(stream: ByteArrayOutputStream, value: Any?) { + when (value) { + is ChartboostPrivacyStandard -> { + stream.write(129) + writeValue(stream, value.raw) + } + else -> super.writeValue(stream, value) + } + } +} + +/** + * The generated classes set the channels to call the methods in the + * corresponding kotlin ChartboostSDKApi interface and swift ChartboostSDKApi + * protocol from the dart layer. + * + * Generated interface from Pigeon that represents a handler of messages from Flutter. + */ +interface ChartboostSDKApi { + /** Used to configure GDPR consent on the Android or iOS Chartboost SDK */ + fun setGDPRConsent(userConsent: Boolean) + /** Used to opt out of the sale of personal information in Chartboost SDK. */ + fun setCCPAConsent(userOptIn: Boolean) + /** Used to clear any of the privacy data use consent above. */ + fun clearDataUseConsent(privacyStandard: ChartboostPrivacyStandard) + + companion object { + /** The codec used by ChartboostSDKApi. */ + val codec: MessageCodec by lazy { + ChartboostSDKApiPigeonCodec() + } + /** Sets up an instance of `ChartboostSDKApi` to handle messages through the `binaryMessenger`. */ + @JvmOverloads + fun setUp(binaryMessenger: BinaryMessenger, api: ChartboostSDKApi?, messageChannelSuffix: String = "") { + val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else "" + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.gma_mediation_chartboost.ChartboostSDKApi.setGDPRConsent$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val userConsentArg = args[0] as Boolean + val wrapped: List = try { + api.setGDPRConsent(userConsentArg) + listOf(null) + } catch (exception: Throwable) { + wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.gma_mediation_chartboost.ChartboostSDKApi.setCCPAConsent$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val userOptInArg = args[0] as Boolean + val wrapped: List = try { + api.setCCPAConsent(userOptInArg) + listOf(null) + } catch (exception: Throwable) { + wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.gma_mediation_chartboost.ChartboostSDKApi.clearDataUseConsent$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val privacyStandardArg = args[0] as ChartboostPrivacyStandard + val wrapped: List = try { + api.clearDataUseConsent(privacyStandardArg) + listOf(null) + } catch (exception: Throwable) { + wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + } + } +} diff --git a/packages/mediation/gma_mediation_chartboost/android/src/main/kotlin/io/flutter/plugins/googlemobileads/mediation/gma_mediation_chartboost/GmaMediationChartboostPlugin.kt b/packages/mediation/gma_mediation_chartboost/android/src/main/kotlin/io/flutter/plugins/googlemobileads/mediation/gma_mediation_chartboost/GmaMediationChartboostPlugin.kt index 4c4b35124..ce5091270 100644 --- a/packages/mediation/gma_mediation_chartboost/android/src/main/kotlin/io/flutter/plugins/googlemobileads/mediation/gma_mediation_chartboost/GmaMediationChartboostPlugin.kt +++ b/packages/mediation/gma_mediation_chartboost/android/src/main/kotlin/io/flutter/plugins/googlemobileads/mediation/gma_mediation_chartboost/GmaMediationChartboostPlugin.kt @@ -14,11 +14,55 @@ package io.flutter.plugins.googlemobileads.mediation.gma_mediation_chartboost +import android.content.Context +import com.chartboost.sdk.Chartboost +import com.chartboost.sdk.privacy.model.CCPA +import com.chartboost.sdk.privacy.model.GDPR import io.flutter.embedding.engine.plugins.FlutterPlugin +import io.flutter.embedding.engine.plugins.activity.ActivityAware +import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding /** Required to link the Android dependency of the Chartboost Adapter. */ -class GmaMediationChartboostPlugin: FlutterPlugin { - override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { } +class GmaMediationChartboostPlugin : FlutterPlugin, ActivityAware, ChartboostSDKApi { + private lateinit var context: Context - override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) { } + override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { + context = flutterPluginBinding.applicationContext + ChartboostSDKApi.setUp(flutterPluginBinding.binaryMessenger, this) + } + + override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) { + ChartboostSDKApi.setUp(binding.binaryMessenger, null) + } + + override fun setGDPRConsent(userConsent: Boolean) { + val dataUserConsent = + if (userConsent) GDPR(GDPR.GDPR_CONSENT.BEHAVIORAL) + else GDPR(GDPR.GDPR_CONSENT.NON_BEHAVIORAL) + Chartboost.addDataUseConsent(context, dataUserConsent) + } + + override fun setCCPAConsent(userOptIn: Boolean) { + val dataUseConsent = + if (userOptIn) CCPA(CCPA.CCPA_CONSENT.OPT_IN_SALE) else CCPA(CCPA.CCPA_CONSENT.OPT_OUT_SALE) + Chartboost.addDataUseConsent(context, dataUseConsent) + } + + override fun clearDataUseConsent(privacyStandard: ChartboostPrivacyStandard) { + val dataUseConsent = when (privacyStandard) { + ChartboostPrivacyStandard.GDPR -> + GDPR(GDPR.GDPR_CONSENT.BEHAVIORAL) + ChartboostPrivacyStandard.CCPA -> + CCPA(CCPA.CCPA_CONSENT.OPT_IN_SALE) + } + Chartboost.clearDataUseConsent(context, dataUseConsent.privacyStandard) + } + + override fun onDetachedFromActivity() {} + + override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {} + + override fun onAttachedToActivity(binding: ActivityPluginBinding) {} + + override fun onDetachedFromActivityForConfigChanges() {} } diff --git a/packages/mediation/gma_mediation_chartboost/android/src/test/kotlin/io/flutter/plugins/googlemobileads/mediation/gma_mediation_chartboost/GmaMediationChartboostPluginTest.kt b/packages/mediation/gma_mediation_chartboost/android/src/test/kotlin/io/flutter/plugins/googlemobileads/mediation/gma_mediation_chartboost/GmaMediationChartboostPluginTest.kt index d929f89a2..ef252ba22 100644 --- a/packages/mediation/gma_mediation_chartboost/android/src/test/kotlin/io/flutter/plugins/googlemobileads/mediation/gma_mediation_chartboost/GmaMediationChartboostPluginTest.kt +++ b/packages/mediation/gma_mediation_chartboost/android/src/test/kotlin/io/flutter/plugins/googlemobileads/mediation/gma_mediation_chartboost/GmaMediationChartboostPluginTest.kt @@ -14,4 +14,118 @@ package io.flutter.plugins.googlemobileads.mediation.gma_mediation_chartboost -internal class GmaMediationChartboostPluginTest {} +import android.content.Context +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.chartboost.sdk.Chartboost +import com.chartboost.sdk.privacy.model.CCPA +import com.chartboost.sdk.privacy.model.GDPR +import io.flutter.embedding.engine.plugins.FlutterPlugin +import io.flutter.plugin.common.BinaryMessenger +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mockito.mockStatic +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.eq +import org.mockito.kotlin.mock + +@RunWith(AndroidJUnit4::class) +internal class GmaMediationChartboostPluginTest { + private val context = ApplicationProvider.getApplicationContext() + private val mockBinaryMessenger = mock() + private val mockFlutterPluginBinding = + mock { + on { applicationContext } doReturn context + on { binaryMessenger } doReturn mockBinaryMessenger + } + + @Test + fun setGDPRConsent_withTrueValue_addsDataUserConsentAsBehavioral() { + val plugin = GmaMediationChartboostPlugin() + mockStatic(ChartboostSDKApi::class.java).use { mockedchartboostSDKApi -> + plugin.onAttachedToEngine(mockFlutterPluginBinding) + val expectedDataUserConsent = GDPR(GDPR.GDPR_CONSENT.BEHAVIORAL) + + plugin.setGDPRConsent(true) + + mockedchartboostSDKApi.verify { + Chartboost.addDataUseConsent(eq(context), eq(expectedDataUserConsent)) + } + } + } + + @Test + fun setGDPRConsent_withFalseValue_addsDataUserConsentAsNonBehavioral() { + val plugin = GmaMediationChartboostPlugin() + mockStatic(ChartboostSDKApi::class.java).use { mockedchartboostSDKApi -> + plugin.onAttachedToEngine(mockFlutterPluginBinding) + val expectedDataUserConsent = GDPR(GDPR.GDPR_CONSENT.NON_BEHAVIORAL) + + plugin.setGDPRConsent(false) + + mockedchartboostSDKApi.verify { + Chartboost.addDataUseConsent(eq(context), eq(expectedDataUserConsent)) + } + } + } + + @Test + fun setCCPAConsent_withTrueValue_addsDataUserConsentAsOptInSale() { + val plugin = GmaMediationChartboostPlugin() + mockStatic(ChartboostSDKApi::class.java).use { mockedchartboostSDKApi -> + plugin.onAttachedToEngine(mockFlutterPluginBinding) + val expectedDataUserConsent = CCPA(CCPA.CCPA_CONSENT.OPT_IN_SALE) + + plugin.setCCPAConsent(true) + + mockedchartboostSDKApi.verify { + Chartboost.addDataUseConsent(eq(context), eq(expectedDataUserConsent)) + } + } + } + + @Test + fun setCCPAConsent_withFalseValue_addsDataUserConsentAsOptOutSale() { + val plugin = GmaMediationChartboostPlugin() + mockStatic(ChartboostSDKApi::class.java).use { mockedchartboostSDKApi -> + plugin.onAttachedToEngine(mockFlutterPluginBinding) + val expectedDataUserConsent = CCPA(CCPA.CCPA_CONSENT.OPT_OUT_SALE) + + plugin.setCCPAConsent(false) + + mockedchartboostSDKApi.verify { + Chartboost.addDataUseConsent(eq(context), eq(expectedDataUserConsent)) + } + } + } + + @Test + fun clearDataUseConsent_usesGDPRPrivacyStandard() { + val plugin = GmaMediationChartboostPlugin() + mockStatic(ChartboostSDKApi::class.java).use { mockedchartboostSDKApi -> + plugin.onAttachedToEngine(mockFlutterPluginBinding) + val expectedPrivacyStandard = GDPR(GDPR.GDPR_CONSENT.NON_BEHAVIORAL).privacyStandard + + plugin.clearDataUseConsent(ChartboostPrivacyStandard.GDPR) + + mockedchartboostSDKApi.verify { + Chartboost.clearDataUseConsent(eq(context), eq(expectedPrivacyStandard)) + } + } + } + + @Test + fun clearDataUseConsent_usesCCPAPrivacyStandard() { + val plugin = GmaMediationChartboostPlugin() + mockStatic(ChartboostSDKApi::class.java).use { mockedchartboostSDKApi -> + plugin.onAttachedToEngine(mockFlutterPluginBinding) + val expectedPrivacyStandard = CCPA(CCPA.CCPA_CONSENT.OPT_OUT_SALE).privacyStandard + + plugin.clearDataUseConsent(ChartboostPrivacyStandard.CCPA) + + mockedchartboostSDKApi.verify { + Chartboost.clearDataUseConsent(eq(context), eq(expectedPrivacyStandard)) + } + } + } +} diff --git a/packages/mediation/gma_mediation_chartboost/example/ios/Flutter/Debug.xcconfig b/packages/mediation/gma_mediation_chartboost/example/ios/Flutter/Debug.xcconfig index 592ceee85..ec97fc6f3 100644 --- a/packages/mediation/gma_mediation_chartboost/example/ios/Flutter/Debug.xcconfig +++ b/packages/mediation/gma_mediation_chartboost/example/ios/Flutter/Debug.xcconfig @@ -1 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" #include "Generated.xcconfig" diff --git a/packages/mediation/gma_mediation_chartboost/example/ios/Flutter/Release.xcconfig b/packages/mediation/gma_mediation_chartboost/example/ios/Flutter/Release.xcconfig index 592ceee85..c4855bfe2 100644 --- a/packages/mediation/gma_mediation_chartboost/example/ios/Flutter/Release.xcconfig +++ b/packages/mediation/gma_mediation_chartboost/example/ios/Flutter/Release.xcconfig @@ -1 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" #include "Generated.xcconfig" diff --git a/packages/mediation/gma_mediation_chartboost/example/ios/Podfile b/packages/mediation/gma_mediation_chartboost/example/ios/Podfile new file mode 100644 index 000000000..d97f17e22 --- /dev/null +++ b/packages/mediation/gma_mediation_chartboost/example/ios/Podfile @@ -0,0 +1,44 @@ +# Uncomment this line to define a global platform for your project +# platform :ios, '12.0' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_ios_podfile_setup + +target 'Runner' do + use_frameworks! + use_modular_headers! + + flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) + target 'RunnerTests' do + inherit! :search_paths + end +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_ios_build_settings(target) + end +end diff --git a/packages/mediation/gma_mediation_chartboost/example/ios/Runner.xcodeproj/project.pbxproj b/packages/mediation/gma_mediation_chartboost/example/ios/Runner.xcodeproj/project.pbxproj index cdc9a5205..d8a506c51 100644 --- a/packages/mediation/gma_mediation_chartboost/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/mediation/gma_mediation_chartboost/example/ios/Runner.xcodeproj/project.pbxproj @@ -8,7 +8,6 @@ /* Begin PBXBuildFile section */ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; - 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; @@ -42,7 +41,7 @@ /* Begin PBXFileReference section */ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; - 331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; + 331C807B294A618700263BE5 /* GmaMediationChartboostPluginTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GmaMediationChartboostPluginTests.swift; sourceTree = ""; }; 331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; @@ -65,17 +64,31 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + BFFBA253CC1FF5F26AA661ED /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 331C8082294A63A400263BE5 /* RunnerTests */ = { isa = PBXGroup; children = ( - 331C807B294A618700263BE5 /* RunnerTests.swift */, + 331C807B294A618700263BE5 /* GmaMediationChartboostPluginTests.swift */, ); path = RunnerTests; sourceTree = ""; }; + 402C0A3BCF5850D9295D83F2 /* Pods */ = { + isa = PBXGroup; + children = ( + ); + path = Pods; + sourceTree = ""; + }; 9740EEB11CF90186004384FC /* Flutter */ = { isa = PBXGroup; children = ( @@ -94,6 +107,7 @@ 97C146F01CF9000F007C117D /* Runner */, 97C146EF1CF9000F007C117D /* Products */, 331C8082294A63A400263BE5 /* RunnerTests */, + 402C0A3BCF5850D9295D83F2 /* Pods */, ); sourceTree = ""; }; @@ -130,6 +144,7 @@ buildPhases = ( 331C807D294A63A400263BE5 /* Sources */, 331C807F294A63A400263BE5 /* Resources */, + BFFBA253CC1FF5F26AA661ED /* Frameworks */, ); buildRules = ( ); @@ -260,7 +275,6 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/packages/mediation/gma_mediation_chartboost/example/ios/Runner.xcworkspace/contents.xcworkspacedata b/packages/mediation/gma_mediation_chartboost/example/ios/Runner.xcworkspace/contents.xcworkspacedata index 1d526a16e..21a3cc14c 100644 --- a/packages/mediation/gma_mediation_chartboost/example/ios/Runner.xcworkspace/contents.xcworkspacedata +++ b/packages/mediation/gma_mediation_chartboost/example/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -4,4 +4,7 @@ + + diff --git a/packages/mediation/gma_mediation_chartboost/example/ios/RunnerTests/GmaMediationChartboostPluginTest.swift b/packages/mediation/gma_mediation_chartboost/example/ios/RunnerTests/GmaMediationChartboostPluginTest.swift deleted file mode 100644 index ccc66805e..000000000 --- a/packages/mediation/gma_mediation_chartboost/example/ios/RunnerTests/GmaMediationChartboostPluginTest.swift +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2024 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import UIKit -import XCTest - -@testable import gma_mediation_chartboost - -class GmaMediationChartboostPluginTests: XCTestCase { } diff --git a/packages/mediation/gma_mediation_chartboost/example/ios/RunnerTests/GmaMediationChartboostPluginTests.swift b/packages/mediation/gma_mediation_chartboost/example/ios/RunnerTests/GmaMediationChartboostPluginTests.swift new file mode 100644 index 000000000..958cd5d10 --- /dev/null +++ b/packages/mediation/gma_mediation_chartboost/example/ios/RunnerTests/GmaMediationChartboostPluginTests.swift @@ -0,0 +1,63 @@ +// Copyright 2024 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import UIKit +import XCTest + +@testable import gma_mediation_chartboost + +class GmaMediationChartboostPluginTests: XCTestCase { + func testSetGDPRConsent() { + let chartboostApiFake = ChartboostApiFake() + + GmaMediationChartboostPlugin(chartboostApi: chartboostApiFake).setGDPRConsent(gdprConsent: true) + + XCTAssertTrue(chartboostApiFake.gdprConsent) + } + + func testSetCCPAConsent() { + let chartboostApiFake = ChartboostApiFake() + + GmaMediationChartboostPlugin(chartboostApi: chartboostApiFake).setCCPAConsent(ccpaConsent: true) + + XCTAssertTrue(chartboostApiFake.ccpaConsent) + } + + func testClearDataUseConsent() { + let chartboostApiFake = ChartboostApiFake() + + GmaMediationChartboostPlugin(chartboostApi: chartboostApiFake) + .clearDataUseConsent(privacyStandard: .gdpr) + + XCTAssertEqual(chartboostApiFake.clearDataUse, 1) + } +} + +class ChartboostApiFake: ChartboostApiProtocol { + var gdprConsent: Bool = false + var ccpaConsent: Bool = false + var clearDataUse: Int = 0 + + func setGDPRConsent(gdprConsent: Bool) { + self.gdprConsent = gdprConsent + } + + func setCCPAConsent(ccpaConsent: Bool) { + self.ccpaConsent = ccpaConsent + } + + func clearDataUseConsent(privacyStandard: ChartboostPrivacyStandard) throws { + self.clearDataUse += 1 + } +} diff --git a/packages/mediation/gma_mediation_chartboost/ios/Classes/ChartboostSDKApi.g.swift b/packages/mediation/gma_mediation_chartboost/ios/Classes/ChartboostSDKApi.g.swift new file mode 100644 index 000000000..465f80790 --- /dev/null +++ b/packages/mediation/gma_mediation_chartboost/ios/Classes/ChartboostSDKApi.g.swift @@ -0,0 +1,181 @@ +// Autogenerated from Pigeon (v22.6.2), do not edit directly. +// See also: https://pub.dev/packages/pigeon + +import Foundation + +#if os(iOS) + import Flutter +#elseif os(macOS) + import FlutterMacOS +#else + #error("Unsupported platform.") +#endif + +/// Error class for passing custom error details to Dart side. +final class PigeonError: Error { + let code: String + let message: String? + let details: Any? + + init(code: String, message: String?, details: Any?) { + self.code = code + self.message = message + self.details = details + } + + var localizedDescription: String { + return + "PigeonError(code: \(code), message: \(message ?? ""), details: \(details ?? "")" + } +} + +private func wrapResult(_ result: Any?) -> [Any?] { + return [result] +} + +private func wrapError(_ error: Any) -> [Any?] { + if let pigeonError = error as? PigeonError { + return [ + pigeonError.code, + pigeonError.message, + pigeonError.details, + ] + } + if let flutterError = error as? FlutterError { + return [ + flutterError.code, + flutterError.message, + flutterError.details, + ] + } + return [ + "\(error)", + "\(type(of: error))", + "Stacktrace: \(Thread.callStackSymbols)", + ] +} + +private func isNullish(_ value: Any?) -> Bool { + return value is NSNull || value == nil +} + +private func nilOrValue(_ value: Any?) -> T? { + if value is NSNull { return nil } + return value as! T? +} + +enum ChartboostPrivacyStandard: Int { + case gDPR = 0 + case cCPA = 1 +} + +private class ChartboostSDKApiPigeonCodecReader: FlutterStandardReader { + override func readValue(ofType type: UInt8) -> Any? { + switch type { + case 129: + let enumResultAsInt: Int? = nilOrValue(self.readValue() as! Int?) + if let enumResultAsInt = enumResultAsInt { + return ChartboostPrivacyStandard(rawValue: enumResultAsInt) + } + return nil + default: + return super.readValue(ofType: type) + } + } +} + +private class ChartboostSDKApiPigeonCodecWriter: FlutterStandardWriter { + override func writeValue(_ value: Any) { + if let value = value as? ChartboostPrivacyStandard { + super.writeByte(129) + super.writeValue(value.rawValue) + } else { + super.writeValue(value) + } + } +} + +private class ChartboostSDKApiPigeonCodecReaderWriter: FlutterStandardReaderWriter { + override func reader(with data: Data) -> FlutterStandardReader { + return ChartboostSDKApiPigeonCodecReader(data: data) + } + + override func writer(with data: NSMutableData) -> FlutterStandardWriter { + return ChartboostSDKApiPigeonCodecWriter(data: data) + } +} + +class ChartboostSDKApiPigeonCodec: FlutterStandardMessageCodec, @unchecked Sendable { + static let shared = ChartboostSDKApiPigeonCodec(readerWriter: ChartboostSDKApiPigeonCodecReaderWriter()) +} + +/// The generated classes set the channels to call the methods in the +/// corresponding kotlin ChartboostSDKApi interface and swift ChartboostSDKApi +/// protocol from the dart layer. +/// +/// Generated protocol from Pigeon that represents a handler of messages from Flutter. +protocol ChartboostSDKApi { + /// Used to configure GDPR consent on the Android or iOS Chartboost SDK + func setGDPRConsent(userConsent: Bool) throws + /// Used to opt out of the sale of personal information in Chartboost SDK. + func setCCPAConsent(userOptIn: Bool) throws + /// Used to clear any of the privacy data use consent above. + func clearDataUseConsent(privacyStandard: ChartboostPrivacyStandard) throws +} + +/// Generated setup class from Pigeon to handle messages through the `binaryMessenger`. +class ChartboostSDKApiSetup { + static var codec: FlutterStandardMessageCodec { ChartboostSDKApiPigeonCodec.shared } + /// Sets up an instance of `ChartboostSDKApi` to handle messages through the `binaryMessenger`. + static func setUp(binaryMessenger: FlutterBinaryMessenger, api: ChartboostSDKApi?, messageChannelSuffix: String = "") { + let channelSuffix = messageChannelSuffix.count > 0 ? ".\(messageChannelSuffix)" : "" + /// Used to configure GDPR consent on the Android or iOS Chartboost SDK + let setGDPRConsentChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.gma_mediation_chartboost.ChartboostSDKApi.setGDPRConsent\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + setGDPRConsentChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let userConsentArg = args[0] as! Bool + do { + try api.setGDPRConsent(userConsent: userConsentArg) + reply(wrapResult(nil)) + } catch { + reply(wrapError(error)) + } + } + } else { + setGDPRConsentChannel.setMessageHandler(nil) + } + /// Used to opt out of the sale of personal information in Chartboost SDK. + let setCCPAConsentChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.gma_mediation_chartboost.ChartboostSDKApi.setCCPAConsent\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + setCCPAConsentChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let userOptInArg = args[0] as! Bool + do { + try api.setCCPAConsent(userOptIn: userOptInArg) + reply(wrapResult(nil)) + } catch { + reply(wrapError(error)) + } + } + } else { + setCCPAConsentChannel.setMessageHandler(nil) + } + /// Used to clear any of the privacy data use consent above. + let clearDataUseConsentChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.gma_mediation_chartboost.ChartboostSDKApi.clearDataUseConsent\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + clearDataUseConsentChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let privacyStandardArg = args[0] as! ChartboostPrivacyStandard + do { + try api.clearDataUseConsent(privacyStandard: privacyStandardArg) + reply(wrapResult(nil)) + } catch { + reply(wrapError(error)) + } + } + } else { + clearDataUseConsentChannel.setMessageHandler(nil) + } + } +} diff --git a/packages/mediation/gma_mediation_chartboost/ios/Classes/GmaMediationChartboostPlugin.swift b/packages/mediation/gma_mediation_chartboost/ios/Classes/GmaMediationChartboostPlugin.swift index ef294f815..b23f2ff2a 100644 --- a/packages/mediation/gma_mediation_chartboost/ios/Classes/GmaMediationChartboostPlugin.swift +++ b/packages/mediation/gma_mediation_chartboost/ios/Classes/GmaMediationChartboostPlugin.swift @@ -12,10 +12,74 @@ // See the License for the specific language governing permissions and // limitations under the License. +import ChartboostSDK import Flutter import UIKit -/** Required to link the iOS dependency of the Chartboost Adapter. */ -public class GmaMediationChartboostPlugin: NSObject, FlutterPlugin { - public static func register(with registrar: FlutterPluginRegistrar) { } +/// Required to link the iOS dependency of the Chartboost Adapter. +public class GmaMediationChartboostPlugin: NSObject, FlutterPlugin, ChartboostSDKApi { + let chartboostApi: ChartboostApiProtocol + + init(chartboostApi: ChartboostApiProtocol) { + self.chartboostApi = chartboostApi + } + + public static func register(with registrar: FlutterPluginRegistrar) { + let messenger: FlutterBinaryMessenger = registrar.messenger() + let api: ChartboostSDKApi & NSObjectProtocol = GmaMediationChartboostPlugin( + chartboostApi: ChartboostApiImpl()) + ChartboostSDKApiSetup.setUp(binaryMessenger: messenger, api: api) + } + + public func detachFromEngine(for registrar: FlutterPluginRegistrar) { + let messenger: FlutterBinaryMessenger = registrar.messenger() + ChartboostSDKApiSetup.setUp(binaryMessenger: messenger, api: nil) + } + + func setGDPRConsent(userConsent: Bool) { + chartboostApi.setGDPRConsent(gdprConsent: userConsent) + } + + func setCCPAConsent(userOptIn userConsent: Bool) { + chartboostApi.setCCPAConsent(ccpaConsent: userConsent) + } + + func clearDataUseConsent(privacyStandard: ChartboostPrivacyStandard) throws { + try chartboostApi.clearDataUseConsent(privacyStandard: privacyStandard) + } +} + +protocol ChartboostApiProtocol { + func setGDPRConsent(gdprConsent: Bool) + func setCCPAConsent(ccpaConsent: Bool) + func clearDataUseConsent(privacyStandard: ChartboostPrivacyStandard) throws +} + +class ChartboostApiImpl: ChartboostApiProtocol { + + func setGDPRConsent(gdprConsent: Bool) { + Chartboost.addDataUseConsent( + CHBDataUseConsent.GDPR( + gdprConsent + ? CHBDataUseConsent.GDPR.Consent.behavioral : CHBDataUseConsent.GDPR.Consent.nonBehavioral + ) + ) + } + + func setCCPAConsent(ccpaConsent: Bool) { + Chartboost.addDataUseConsent( + CHBDataUseConsent.CCPA( + ccpaConsent + ? CHBDataUseConsent.CCPA.Consent.optInSale : CHBDataUseConsent.CCPA.Consent.optOutSale) + ) + } + + func clearDataUseConsent(privacyStandard: ChartboostPrivacyStandard) throws { + switch privacyStandard { + case .gDPR: + Chartboost.clearDataUseConsent(for: CHBPrivacyStandard.GDPR) + case .cCPA: + Chartboost.clearDataUseConsent(for: CHBPrivacyStandard.CCPA) + } + } } diff --git a/packages/mediation/gma_mediation_chartboost/lib/chartboost_sdk_api.g.dart b/packages/mediation/gma_mediation_chartboost/lib/chartboost_sdk_api.g.dart new file mode 100644 index 000000000..42dded3ce --- /dev/null +++ b/packages/mediation/gma_mediation_chartboost/lib/chartboost_sdk_api.g.dart @@ -0,0 +1,143 @@ +// Autogenerated from Pigeon (v22.6.2), do not edit directly. +// See also: https://pub.dev/packages/pigeon +// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import, no_leading_underscores_for_local_identifiers + +import 'dart:async'; +import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; + +import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer; +import 'package:flutter/services.dart'; + +PlatformException _createConnectionError(String channelName) { + return PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel: "$channelName".', + ); +} + +enum ChartboostPrivacyStandard { + GDPR, + CCPA, +} + +class _PigeonCodec extends StandardMessageCodec { + const _PigeonCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is int) { + buffer.putUint8(4); + buffer.putInt64(value); + } else if (value is ChartboostPrivacyStandard) { + buffer.putUint8(129); + writeValue(buffer, value.index); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 129: + final int? value = readValue(buffer) as int?; + return value == null ? null : ChartboostPrivacyStandard.values[value]; + default: + return super.readValueOfType(type, buffer); + } + } +} + +/// The generated classes set the channels to call the methods in the +/// corresponding kotlin ChartboostSDKApi interface and swift ChartboostSDKApi +/// protocol from the dart layer. +class ChartboostSDKApi { + /// Constructor for [ChartboostSDKApi]. The [binaryMessenger] named argument is + /// available for dependency injection. If it is left null, the default + /// BinaryMessenger will be used which routes to the host platform. + ChartboostSDKApi( + {BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) + : pigeonVar_binaryMessenger = binaryMessenger, + pigeonVar_messageChannelSuffix = + messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + final BinaryMessenger? pigeonVar_binaryMessenger; + + static const MessageCodec pigeonChannelCodec = _PigeonCodec(); + + final String pigeonVar_messageChannelSuffix; + + /// Used to configure GDPR consent on the Android or iOS Chartboost SDK + Future setGDPRConsent(bool userConsent) async { + final String pigeonVar_channelName = + 'dev.flutter.pigeon.gma_mediation_chartboost.ChartboostSDKApi.setGDPRConsent$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final List? pigeonVar_replyList = + await pigeonVar_channel.send([userConsent]) as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else { + return; + } + } + + /// Used to opt out of the sale of personal information in Chartboost SDK. + Future setCCPAConsent(bool userOptIn) async { + final String pigeonVar_channelName = + 'dev.flutter.pigeon.gma_mediation_chartboost.ChartboostSDKApi.setCCPAConsent$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final List? pigeonVar_replyList = + await pigeonVar_channel.send([userOptIn]) as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else { + return; + } + } + + /// Used to clear any of the privacy data use consent above. + Future clearDataUseConsent( + ChartboostPrivacyStandard privacyStandard) async { + final String pigeonVar_channelName = + 'dev.flutter.pigeon.gma_mediation_chartboost.ChartboostSDKApi.clearDataUseConsent$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final List? pigeonVar_replyList = await pigeonVar_channel + .send([privacyStandard]) as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else { + return; + } + } +} diff --git a/packages/mediation/gma_mediation_chartboost/lib/gma_mediation_chartboost.dart b/packages/mediation/gma_mediation_chartboost/lib/gma_mediation_chartboost.dart index 666426317..bcd73fdde 100644 --- a/packages/mediation/gma_mediation_chartboost/lib/gma_mediation_chartboost.dart +++ b/packages/mediation/gma_mediation_chartboost/lib/gma_mediation_chartboost.dart @@ -12,5 +12,20 @@ // See the License for the specific language governing permissions and // limitations under the License. -/// This empty class is needed to allow correct compatibility analysis with host platforms. -class GmaMediationChartboost {} +import 'package:gma_mediation_chartboost/chartboost_sdk_api.g.dart'; + +/// This class has entrypoint to call Chartboost SDK APIs. +class GmaMediationChartboost { + Future setGDPRConsent(bool userConsent) async { + ChartboostSDKApi().setGDPRConsent(userConsent); + } + + Future setCCPAConsent(bool userOptIn) async { + ChartboostSDKApi().setCCPAConsent(userOptIn); + } + + Future clearDataUseConsent( + ChartboostPrivacyStandard privacyStandard) async { + ChartboostSDKApi().clearDataUseConsent(privacyStandard); + } +} diff --git a/packages/mediation/gma_mediation_chartboost/pigeons/chartboost_sdk_api.dart b/packages/mediation/gma_mediation_chartboost/pigeons/chartboost_sdk_api.dart index 2dee582e4..e60c402e1 100644 --- a/packages/mediation/gma_mediation_chartboost/pigeons/chartboost_sdk_api.dart +++ b/packages/mediation/gma_mediation_chartboost/pigeons/chartboost_sdk_api.dart @@ -14,6 +14,11 @@ import 'package:pigeon/pigeon.dart'; +enum ChartboostPrivacyStandard { + GDPR, + CCPA, +} + @ConfigurePigeon( PigeonOptions( dartOut: 'lib/chartboost_sdk_api.g.dart', @@ -31,5 +36,16 @@ import 'package:pigeon/pigeon.dart'; ) @HostApi() -/// The generated classes set the channels to call the methods in the corresponding kotlin ChartboostSDKApi interface and swift ChartboostSDKApi protocol from the dart layer. -abstract class ChartboostSDKApi {} +/// The generated classes set the channels to call the methods in the +/// corresponding kotlin ChartboostSDKApi interface and swift ChartboostSDKApi +/// protocol from the dart layer. +abstract class ChartboostSDKApi { + /// Used to configure GDPR consent on the Android or iOS Chartboost SDK + void setGDPRConsent(bool userConsent); + + /// Used to opt out of the sale of personal information in Chartboost SDK. + void setCCPAConsent(bool userOptIn); + + /// Used to clear any of the privacy data use consent above. + void clearDataUseConsent(ChartboostPrivacyStandard privacyStandard); +}