Skip to content

Commit 0baba2e

Browse files
authored
Added Privacy API implementation for DT Exchange Flutter Adapter (#1056)
* Added DT Exchange Privacy API usage * Added Android and iOS jobs to the DT Exchange github action * Updated iOS versions to run swift tests
1 parent fe544bf commit 0baba2e

File tree

18 files changed

+965
-64
lines changed

18 files changed

+965
-64
lines changed

.github/workflows/gma_mediation_dtexchange.yaml

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,57 @@ on:
2626
- main
2727

2828
jobs:
29+
android:
30+
runs-on: macos-latest
31+
if: github.event_name == 'pull_request'
32+
timeout-minutes: 30
33+
steps:
34+
- uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633
35+
with:
36+
fetch-depth: 0
37+
- uses: actions/setup-java@v4
38+
with:
39+
distribution: 'temurin'
40+
java-version: '17'
41+
- name: "Install Flutter"
42+
run: ./.github/workflows/scripts/install-flutter.sh stable
43+
- name: "Install Tools"
44+
run: ./.github/workflows/scripts/install-tools.sh
45+
- name: "Build Example"
46+
run: ./.github/workflows/scripts/build-example.sh android ./lib/main.dart packages/mediation/gma_mediation_dtexchange/example
47+
- name: "Unit Tests"
48+
run: |
49+
cd packages/mediation/gma_mediation_dtexchange/example/android
50+
./gradlew :gma_mediation_dtexchange:testDebugUnitTest
51+
52+
iOS:
53+
runs-on: macos-latest
54+
timeout-minutes: 40
55+
steps:
56+
- uses: swift-actions/setup-swift@v2
57+
with:
58+
swift-version: "5.7.2"
59+
- uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633
60+
with:
61+
fetch-depth: 0
62+
- name: "Install Flutter"
63+
run: ./.github/workflows/scripts/install-flutter.sh stable
64+
- name: "Install Tools"
65+
run: |
66+
./.github/workflows/scripts/install-tools.sh
67+
- name: "Unit Tests"
68+
run: |
69+
cd packages/mediation/gma_mediation_dtexchange/example/ios
70+
flutter clean
71+
flutter pub get
72+
flutter precache --ios
73+
pod install
74+
xcodebuild -configuration Debug -resultBundlePath TestResults VERBOSE_SCRIPT_LOGGING=YES -workspace Runner.xcworkspace -scheme Runner -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 15,OS=17.5' test
75+
- uses: actions/upload-artifact@v4
76+
if: failure()
77+
with:
78+
name: iOSTestResults
79+
path: packages/mediation/gma_mediation_dtexchange/example/ios/TestResults.xcresult
2980
flutter:
3081
runs-on: ubuntu-latest
3182
if: github.event_name == 'pull_request'

packages/mediation/gma_mediation_dtexchange/android/build.gradle

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

5252
dependencies {
5353
implementation 'com.google.ads.mediation:fyber:8.2.7.0'
54-
testImplementation 'org.jetbrains.kotlin:kotlin-test'
55-
testImplementation 'org.mockito:mockito-core:5.0.0'
54+
testImplementation 'junit:junit:4.13.2'
55+
testImplementation 'androidx.test:core:1.5.0'
56+
testImplementation 'androidx.test:core-ktx:1.5.0'
57+
testImplementation 'androidx.test.ext:junit:1.1.5'
58+
testImplementation 'org.jetbrains.kotlin:kotlin-stdlib:1.8.20'
59+
testImplementation 'org.mockito:mockito-core:5.5.0'
60+
testImplementation 'org.mockito.kotlin:mockito-kotlin:5.1.0'
61+
testImplementation 'org.robolectric:robolectric:4.10.3'
5662
}
5763

5864
testOptions {
5965
unitTests.all {
60-
useJUnitPlatform()
66+
useJUnit()
6167

6268
testLogging {
63-
events "passed", "skipped", "failed", "standardOut", "standardError"
64-
outputs.upToDateWhen {false}
65-
showStandardStreams = true
69+
events "passed", "skipped", "failed", "standardOut", "standardError"
70+
outputs.upToDateWhen {false}
71+
showStandardStreams = true
6672
}
6773
}
74+
unitTests {
75+
includeAndroidResources = true
76+
unitTests.returnDefaultValues = true
77+
}
6878
}
6979
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
// Autogenerated from Pigeon (v19.0.0), 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_dtexchange
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+
* The generated classes set the channels to call the methods in the corresponding kotlin DTExchangePrivacyApi interface and swift DTExchangePrivacyApi protocol from the dart layer.
48+
*
49+
* Generated interface from Pigeon that represents a handler of messages from Flutter.
50+
*/
51+
interface DTExchangePrivacyApi {
52+
/** Used to configure LGDP on the Android or iOS DTExchange SDK. */
53+
fun setLgpdConsent(wasConsentGiven: Boolean)
54+
/** Used to clear the LGDP flag on the Android or iOS DTExchange SDK. */
55+
fun clearLgpdConsentData()
56+
/** Used to configure consent to Sell Personal Information on the Android or iOS DTExchange SDK. */
57+
fun setUSPrivacyString(usPrivacyString: String)
58+
/** Used to clear the US Privacy flag on the Android or iOS DTExchange SDK. */
59+
fun clearUSPrivacyString()
60+
61+
companion object {
62+
/** The codec used by DTExchangePrivacyApi. */
63+
val codec: MessageCodec<Any?> by lazy {
64+
StandardMessageCodec()
65+
}
66+
/** Sets up an instance of `DTExchangePrivacyApi` to handle messages through the `binaryMessenger`. */
67+
fun setUp(binaryMessenger: BinaryMessenger, api: DTExchangePrivacyApi?, messageChannelSuffix: String = "") {
68+
val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else ""
69+
run {
70+
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.gma_mediation_dtexchange.DTExchangePrivacyApi.setLgpdConsent$separatedMessageChannelSuffix", codec)
71+
if (api != null) {
72+
channel.setMessageHandler { message, reply ->
73+
val args = message as List<Any?>
74+
val wasConsentGivenArg = args[0] as Boolean
75+
val wrapped: List<Any?> = try {
76+
api.setLgpdConsent(wasConsentGivenArg)
77+
listOf<Any?>(null)
78+
} catch (exception: Throwable) {
79+
wrapError(exception)
80+
}
81+
reply.reply(wrapped)
82+
}
83+
} else {
84+
channel.setMessageHandler(null)
85+
}
86+
}
87+
run {
88+
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.gma_mediation_dtexchange.DTExchangePrivacyApi.clearLgpdConsentData$separatedMessageChannelSuffix", codec)
89+
if (api != null) {
90+
channel.setMessageHandler { _, reply ->
91+
val wrapped: List<Any?> = try {
92+
api.clearLgpdConsentData()
93+
listOf<Any?>(null)
94+
} catch (exception: Throwable) {
95+
wrapError(exception)
96+
}
97+
reply.reply(wrapped)
98+
}
99+
} else {
100+
channel.setMessageHandler(null)
101+
}
102+
}
103+
run {
104+
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.gma_mediation_dtexchange.DTExchangePrivacyApi.setUSPrivacyString$separatedMessageChannelSuffix", codec)
105+
if (api != null) {
106+
channel.setMessageHandler { message, reply ->
107+
val args = message as List<Any?>
108+
val usPrivacyStringArg = args[0] as String
109+
val wrapped: List<Any?> = try {
110+
api.setUSPrivacyString(usPrivacyStringArg)
111+
listOf<Any?>(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_dtexchange.DTExchangePrivacyApi.clearUSPrivacyString$separatedMessageChannelSuffix", codec)
123+
if (api != null) {
124+
channel.setMessageHandler { _, reply ->
125+
val wrapped: List<Any?> = try {
126+
api.clearUSPrivacyString()
127+
listOf<Any?>(null)
128+
} catch (exception: Throwable) {
129+
wrapError(exception)
130+
}
131+
reply.reply(wrapped)
132+
}
133+
} else {
134+
channel.setMessageHandler(null)
135+
}
136+
}
137+
}
138+
}
139+
}
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,31 @@
11
package io.flutter.plugins.googlemobileads.mediation.gma_mediation_dtexchange
22

3+
import com.fyber.inneractive.sdk.external.InneractiveAdManager
34
import io.flutter.embedding.engine.plugins.FlutterPlugin
45

5-
/** Class that serves as bridge to get the adapter android dependency and make it available to a Flutter app. */
6-
class GmaMediationDTExchangePlugin: FlutterPlugin {
7-
override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {}
6+
/** Manages DTExchangePrivacyApi and implements the needed methods. */
7+
class GmaMediationDTExchangePlugin: FlutterPlugin, DTExchangePrivacyApi {
8+
override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
9+
DTExchangePrivacyApi.setUp(flutterPluginBinding.binaryMessenger, this)
10+
}
811

9-
override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {}
12+
override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
13+
DTExchangePrivacyApi.setUp(binding.binaryMessenger, null)
14+
}
15+
16+
override fun setLgpdConsent(wasConsentGiven: Boolean) {
17+
InneractiveAdManager.setLgpdConsent(wasConsentGiven)
18+
}
19+
20+
override fun clearLgpdConsentData() {
21+
InneractiveAdManager.clearLgpdConsentData()
22+
}
23+
24+
override fun setUSPrivacyString(usPrivacyString: String) {
25+
InneractiveAdManager.setUSPrivacyString(usPrivacyString)
26+
}
27+
28+
override fun clearUSPrivacyString() {
29+
InneractiveAdManager.clearUSPrivacyString()
30+
}
1031
}
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,80 @@
11
package io.flutter.plugins.googlemobileads.mediation.gma_mediation_dtexchange
22

3-
import io.flutter.plugin.common.MethodCall
4-
import io.flutter.plugin.common.MethodChannel
5-
import kotlin.test.Test
6-
import org.mockito.Mockito
7-
8-
/*
9-
* This demonstrates a simple unit test of the Kotlin portion of this plugin's implementation.
10-
*
11-
* Once you have built the plugin's example app, you can run these tests from the command
12-
* line by running `./gradlew testDebugUnitTest` in the `example/android/` directory, or
13-
* you can run them directly from IDEs that support JUnit such as Android Studio.
14-
*/
3+
import androidx.test.ext.junit.runners.AndroidJUnit4
4+
import com.fyber.inneractive.sdk.external.InneractiveAdManager
5+
import org.junit.Test
6+
import org.junit.runner.RunWith
7+
import org.mockito.Mockito.mockStatic
8+
import org.mockito.kotlin.eq
159

10+
@RunWith(AndroidJUnit4::class)
1611
internal class GmaMediationDtexchangePluginTest {
1712
@Test
18-
fun onMethodCall_getPlatformVersion_returnsExpectedValue() {
19-
val plugin = GmaMediationDtexchangePlugin()
13+
fun setLgpdConsent_withTrueValue_invokesSetLgpdConsentWithTrueValue() {
14+
val plugin = GmaMediationDTExchangePlugin()
15+
mockStatic(InneractiveAdManager::class.java).use { mockedDTExchangeAdManager ->
2016

21-
val call = MethodCall("getPlatformVersion", null)
22-
val mockResult: MethodChannel.Result = Mockito.mock(MethodChannel.Result::class.java)
23-
plugin.onMethodCall(call, mockResult)
17+
plugin.setLgpdConsent(true)
2418

25-
Mockito.verify(mockResult).success("Android " + android.os.Build.VERSION.RELEASE)
19+
mockedDTExchangeAdManager.verify {
20+
InneractiveAdManager.setLgpdConsent(eq(true))
21+
}
22+
}
23+
}
24+
25+
@Test
26+
fun setLgpdConsent_withFalseValue_invokesSetLgpdConsentWithFalseValue() {
27+
val plugin = GmaMediationDTExchangePlugin()
28+
mockStatic(InneractiveAdManager::class.java).use { mockedDTExchangeAdManager ->
29+
30+
plugin.setLgpdConsent(false)
31+
32+
mockedDTExchangeAdManager.verify {
33+
InneractiveAdManager.setLgpdConsent(eq(false))
34+
}
35+
}
36+
}
37+
38+
@Test
39+
fun clearLgpdConsentData_invokesClearLgpdConsentData() {
40+
val plugin = GmaMediationDTExchangePlugin()
41+
mockStatic(InneractiveAdManager::class.java).use { mockedDTExchangeAdManager ->
42+
43+
plugin.clearLgpdConsentData()
44+
45+
mockedDTExchangeAdManager.verify {
46+
InneractiveAdManager.clearLgpdConsentData()
47+
}
48+
}
49+
}
50+
51+
@Test
52+
fun setUSPrivacyString_invokesSetUSPrivacyString() {
53+
val plugin = GmaMediationDTExchangePlugin()
54+
mockStatic(InneractiveAdManager::class.java).use { mockedDTExchangeAdManager ->
55+
56+
plugin.setUSPrivacyString(TEST_CONSENT_STRING)
57+
58+
mockedDTExchangeAdManager.verify {
59+
InneractiveAdManager.setUSPrivacyString(eq(TEST_CONSENT_STRING))
60+
}
61+
}
62+
}
63+
64+
@Test
65+
fun clearUSPrivacyString_invokesClearUSPrivacyString() {
66+
val plugin = GmaMediationDTExchangePlugin()
67+
mockStatic(InneractiveAdManager::class.java).use { mockedDTExchangeAdManager ->
68+
69+
plugin.clearUSPrivacyString()
70+
71+
mockedDTExchangeAdManager.verify {
72+
InneractiveAdManager.clearUSPrivacyString()
73+
}
74+
}
75+
}
76+
77+
companion object {
78+
const val TEST_CONSENT_STRING = "testConsentString"
2679
}
2780
}
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)