Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.embedding.engine.plugins.FlutterPlugin.FlutterAssets
import io.flutter.embedding.engine.plugins.activity.ActivityAware
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding
import com.usercentrics.sdk.UsercentricsEvent
import com.usercentrics.sdk.services.gpp.GppSectionChangePayload
import com.usercentrics.sdk.flutter.serializer.serialize
import io.flutter.plugin.common.EventChannel
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
Expand All @@ -24,6 +28,8 @@ class UsercentricsPlugin : FlutterPlugin,
FlutterAssetsProvider {

private lateinit var channel: MethodChannel
private var gppSectionChangeEventChannel: EventChannel? = null
private var gppSectionChangeSubscription: com.usercentrics.sdk.UsercentricsDisposableEvent<GppSectionChangePayload>? = null
private var activityBinding: ActivityPluginBinding? = null
private var flutterAssets: FlutterAssets? = null

Expand Down Expand Up @@ -63,7 +69,10 @@ class UsercentricsPlugin : FlutterPlugin,
SetABTestingVariantBridge(),
TrackBridge(),
GetAdditionalConsentModeDataBridge(),
ClearUserSessionBridge()
ClearUserSessionBridge(),
GetGPPDataBridge(),
GetGPPStringBridge(),
SetGPPConsentBridge()
).associateBy { it.name }
}

Expand Down Expand Up @@ -94,11 +103,29 @@ class UsercentricsPlugin : FlutterPlugin,
flutterAssets = binding.flutterAssets
channel = MethodChannel(binding.binaryMessenger, "usercentrics")
channel.setMethodCallHandler(this)

gppSectionChangeEventChannel = EventChannel(binding.binaryMessenger, "usercentrics/onGppSectionChange")
gppSectionChangeEventChannel?.setStreamHandler(object : EventChannel.StreamHandler {
override fun onListen(arguments: Any?, events: EventChannel.EventSink?) {
gppSectionChangeSubscription = UsercentricsEvent.onGppSectionChange { payload ->
events?.success(payload.serialize())
}
}

override fun onCancel(arguments: Any?) {
gppSectionChangeSubscription?.dispose()
gppSectionChangeSubscription = null
}
})
}

override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) {
flutterAssets = null
channel.setMethodCallHandler(null)
gppSectionChangeSubscription?.dispose()
gppSectionChangeSubscription = null
gppSectionChangeEventChannel?.setStreamHandler(null)
gppSectionChangeEventChannel = null
}

override fun onAttachedToActivity(activityBinding: ActivityPluginBinding) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.usercentrics.sdk.flutter.bridge

import com.usercentrics.sdk.flutter.api.FlutterMethodCall
import com.usercentrics.sdk.flutter.api.FlutterResult
import com.usercentrics.sdk.flutter.api.UsercentricsProxy
import com.usercentrics.sdk.flutter.api.UsercentricsProxySingleton
import com.usercentrics.sdk.flutter.serializer.serialize

internal class GetGPPDataBridge(
private val usercentrics: UsercentricsProxy = UsercentricsProxySingleton
) : MethodBridge {

override val name: String
get() = "getGPPData"

override fun invoke(call: FlutterMethodCall, result: FlutterResult) {
assert(name == call.method)
result.success(usercentrics.instance.getGPPData().serialize())
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.usercentrics.sdk.flutter.bridge

import com.usercentrics.sdk.flutter.api.FlutterMethodCall
import com.usercentrics.sdk.flutter.api.FlutterResult
import com.usercentrics.sdk.flutter.api.UsercentricsProxy
import com.usercentrics.sdk.flutter.api.UsercentricsProxySingleton

internal class GetGPPStringBridge(
private val usercentrics: UsercentricsProxy = UsercentricsProxySingleton
) : MethodBridge {

override val name: String
get() = "getGPPString"

override fun invoke(call: FlutterMethodCall, result: FlutterResult) {
assert(name == call.method)
result.success(usercentrics.instance.getGPPString())
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.usercentrics.sdk.flutter.bridge

import com.usercentrics.sdk.flutter.api.FlutterMethodCall
import com.usercentrics.sdk.flutter.api.FlutterResult
import com.usercentrics.sdk.flutter.api.UsercentricsProxy
import com.usercentrics.sdk.flutter.api.UsercentricsProxySingleton

internal class SetGPPConsentBridge(
private val usercentrics: UsercentricsProxy = UsercentricsProxySingleton
) : MethodBridge {

override val name: String
get() = "setGPPConsent"

override fun invoke(call: FlutterMethodCall, result: FlutterResult) {
assert(name == call.method)
val argsMap = call.arguments as Map<*, *>
val sectionName = argsMap["sectionName"] as String
val fieldName = argsMap["fieldName"] as String
val value = normalizeValue(argsMap["value"])
usercentrics.instance.setGPPConsent(sectionName, fieldName, value)
result.success(null)
}

private fun normalizeValue(value: Any?): Any {
if (value == null) return Unit
if (value is Double && value % 1.0 == 0.0) return value.toInt()
return value
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,6 @@ private fun TCF2Settings.serialize(): Any {
"disabledSpecialFeatures" to disabledSpecialFeatures,
"firstLayerShowDescriptions" to firstLayerShowDescriptions,
"hideNonIabOnFirstLayer" to hideNonIabOnFirstLayer,
"resurfacePeriodEnded" to resurfacePeriodEnded,
"resurfacePurposeChanged" to resurfacePurposeChanged,
"resurfaceVendorAdded" to resurfaceVendorAdded,
"firstLayerDescription" to firstLayerDescription,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.usercentrics.sdk.flutter.serializer

import com.usercentrics.sdk.services.gpp.GppData
import com.usercentrics.sdk.services.gpp.GppSectionChangePayload

internal fun GppData.serialize(): Any {
val serializedSections = sections.mapValues { (_, fields) ->
fields.mapValues { (_, value) ->
when (value) {
null -> null
is Boolean -> value
is Int -> value
is Double -> value
is String -> value
else -> value.toString()
}
}
}
return mapOf(
"gppString" to gppString,
"applicableSections" to applicableSections,
"sections" to serializedSections
)
}

internal fun GppSectionChangePayload.serialize(): Any {
return mapOf(
"data" to data
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package com.usercentrics.sdk.flutter.bridge

import com.usercentrics.sdk.UsercentricsSDK
import com.usercentrics.sdk.flutter.api.FakeFlutterMethodCall
import com.usercentrics.sdk.flutter.api.FakeFlutterResult
import com.usercentrics.sdk.flutter.api.FakeUsercentricsProxy
import com.usercentrics.sdk.flutter.mock.GetGPPDataMock
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import org.junit.Assert.assertEquals
import org.junit.Assert.assertThrows
import org.junit.Test

class GetGPPDataBridgeTest {

@Test
fun testName() {
val instance = GetGPPDataBridge(FakeUsercentricsProxy())
assertEquals("getGPPData", instance.name)
}

@Test
fun testInvokeWithOtherName() {
val instance = GetGPPDataBridge(FakeUsercentricsProxy())
val call = FakeFlutterMethodCall(method = "otherName", arguments = null)

assertThrows(AssertionError::class.java) {
instance.invoke(call, FakeFlutterResult())
}
}

@Test
fun testInvoke() {
val usercentricsSDK = mockk<UsercentricsSDK>()
every { usercentricsSDK.getGPPData() }.returns(GetGPPDataMock.fake)
val usercentricsProxy = FakeUsercentricsProxy(usercentricsSDK)
val instance = GetGPPDataBridge(usercentricsProxy)
val result = FakeFlutterResult()

instance.invoke(GetGPPDataMock.call, result)

verify(exactly = 1) { usercentricsSDK.getGPPData() }

assertEquals(1, result.successCount)
assertEquals(GetGPPDataMock.expected, result.successResultArgument)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package com.usercentrics.sdk.flutter.bridge

import com.usercentrics.sdk.UsercentricsSDK
import com.usercentrics.sdk.flutter.api.FakeFlutterMethodCall
import com.usercentrics.sdk.flutter.api.FakeFlutterResult
import com.usercentrics.sdk.flutter.api.FakeUsercentricsProxy
import com.usercentrics.sdk.flutter.mock.GetGPPStringMock
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import org.junit.Assert.assertEquals
import org.junit.Assert.assertThrows
import org.junit.Test

class GetGPPStringBridgeTest {

@Test
fun testName() {
val instance = GetGPPStringBridge(FakeUsercentricsProxy())
assertEquals("getGPPString", instance.name)
}

@Test
fun testInvokeWithOtherName() {
val instance = GetGPPStringBridge(FakeUsercentricsProxy())
val call = FakeFlutterMethodCall(method = "otherName", arguments = null)

assertThrows(AssertionError::class.java) {
instance.invoke(call, FakeFlutterResult())
}
}

@Test
fun testInvoke() {
val usercentricsSDK = mockk<UsercentricsSDK>()
every { usercentricsSDK.getGPPString() }.returns(GetGPPStringMock.fake)
val usercentricsProxy = FakeUsercentricsProxy(usercentricsSDK)
val instance = GetGPPStringBridge(usercentricsProxy)
val result = FakeFlutterResult()

instance.invoke(GetGPPStringMock.call, result)

verify(exactly = 1) { usercentricsSDK.getGPPString() }

assertEquals(1, result.successCount)
assertEquals(GetGPPStringMock.expected, result.successResultArgument)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package com.usercentrics.sdk.flutter.bridge

import com.usercentrics.sdk.UsercentricsSDK
import com.usercentrics.sdk.flutter.api.FakeFlutterMethodCall
import com.usercentrics.sdk.flutter.api.FakeFlutterResult
import com.usercentrics.sdk.flutter.api.FakeUsercentricsProxy
import com.usercentrics.sdk.flutter.mock.SetGPPConsentMock
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import org.junit.Assert.assertEquals
import org.junit.Assert.assertThrows
import org.junit.Test

class SetGPPConsentBridgeTest {

@Test
fun testName() {
val instance = SetGPPConsentBridge(FakeUsercentricsProxy())
assertEquals("setGPPConsent", instance.name)
}

@Test
fun testInvokeWithOtherName() {
val instance = SetGPPConsentBridge(FakeUsercentricsProxy())
val call = FakeFlutterMethodCall(method = "otherName", arguments = null)

assertThrows(AssertionError::class.java) {
instance.invoke(call, FakeFlutterResult())
}
}

@Test
fun testInvoke() {
val usercentricsSDK = mockk<UsercentricsSDK>()
every {
usercentricsSDK.setGPPConsent(any(), any(), any())
}.returns(Unit)
val usercentricsProxy = FakeUsercentricsProxy(usercentricsSDK)
val instance = SetGPPConsentBridge(usercentricsProxy)
val result = FakeFlutterResult()

instance.invoke(SetGPPConsentMock.call, result)

verify(exactly = 1) {
usercentricsSDK.setGPPConsent(
SetGPPConsentMock.callSectionName,
SetGPPConsentMock.callFieldName,
SetGPPConsentMock.callValue
)
}

assertEquals(1, result.successCount)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,6 @@ internal object GetCMPDataMock {
tabsVendorsLabel = "Vendors",
labelsIabVendors = "Vendors who are part of the IAB TCF",
buttonsDenyAllLabel = "Deny all",
resurfacePeriodEnded = true,
vendorSpecialPurposes = "Special Purposes",
firstLayerAdditionalInfo = "",
resurfaceVendorAdded = true,
Expand Down Expand Up @@ -502,7 +501,6 @@ internal object GetCMPDataMock {
"disabledSpecialFeatures" to listOf<Any>(),
"firstLayerShowDescriptions" to false,
"hideNonIabOnFirstLayer" to false,
"resurfacePeriodEnded" to true,
"resurfacePurposeChanged" to true,
"resurfaceVendorAdded" to true,
"firstLayerDescription" to "We and our third-party vendors use technologies (e.g. cookies) to store and/or access information on user's devices in order to process personal data such as IP addresses or browsing data. You may consent to the processing of your personal data for the listed purposes below. Alternatively you can set your preferences before consenting or refuse to consent. Please note that some vendors may process your personal data based on their legitimate business interest and do not ask for your consent. To exercise your right to object to processing based on legitimate interest please view our vendorlist.",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.usercentrics.sdk.flutter.mock

import com.usercentrics.sdk.flutter.api.FakeFlutterMethodCall
import com.usercentrics.sdk.services.gpp.GppData

internal object GetGPPDataMock {

val fake = GppData(
gppString = "DBABLA~BVQqAAAACgA.QA",
applicableSections = listOf(7, 8),
sections = mapOf(
"usnat" to mapOf(
"Version" to 1,
"SaleOptOut" to 2,
"GpcSegmentIncluded" to true,
),
),
)

val call = FakeFlutterMethodCall(method = "getGPPData", arguments = null)
val expected = mapOf(
"gppString" to "DBABLA~BVQqAAAACgA.QA",
"applicableSections" to listOf(7, 8),
"sections" to mapOf(
"usnat" to mapOf(
"Version" to 1,
"SaleOptOut" to 2,
"GpcSegmentIncluded" to true,
),
),
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.usercentrics.sdk.flutter.mock

import com.usercentrics.sdk.flutter.api.FakeFlutterMethodCall

internal object GetGPPStringMock {

val fake = "DBABLA~BVQqAAAACgA.QA"

val call = FakeFlutterMethodCall(method = "getGPPString", arguments = null)
val expected = "DBABLA~BVQqAAAACgA.QA"
}
Loading
Loading