Skip to content

Commit

Permalink
Add user consent api.
Browse files Browse the repository at this point in the history
  • Loading branch information
StaehliJ committed Sep 6, 2023
1 parent 156937c commit a98ee19
Show file tree
Hide file tree
Showing 15 changed files with 157 additions and 33 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@
package ch.srgssr.pillarbox.analytics

import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
import ch.srgssr.pillarbox.analytics.comscore.ComScoreLabelInternal
import ch.srgssr.pillarbox.analytics.comscore.ComScoreLabel
import ch.srgssr.pillarbox.analytics.comscore.ComScorePageView
import ch.srgssr.pillarbox.analytics.comscore.ComScoreSrg
import ch.srgssr.pillarbox.analytics.comscore.ComScoreUserConsent
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableSharedFlow
Expand Down Expand Up @@ -41,7 +42,7 @@ class TestComScoreSrg {
val pageTitle = "Title"
val pageView = ComScorePageView(name = pageTitle)
val actualLabels = ArrayList<Map<String, String>>()
val expectedLabels = listOf(mapOf(Pair(ComScoreLabelInternal.C8.label, pageTitle)))
val expectedLabels = listOf(mapOf(Pair(ComScoreLabel.C8.label, pageTitle)))
val job = launch(dispatcher) {
tracker.pageViewFlow.take(1).toList(actualLabels)
}
Expand All @@ -58,7 +59,7 @@ class TestComScoreSrg {
val labels = mapOf(Pair("key1", "value01"), Pair("key2", " "))
val pageView = ComScorePageView(name = pageTitle, labels = labels)
val actualLabels = ArrayList<Map<String, String>>()
val expectedLabels = listOf(mapOf(Pair(ComScoreLabelInternal.C8.label, pageTitle), Pair("key1", "value01")))
val expectedLabels = listOf(mapOf(Pair(ComScoreLabel.C8.label, pageTitle), Pair("key1", "value01")))
val job = launch(dispatcher) {
tracker.pageViewFlow.take(1).toList(actualLabels)
}
Expand All @@ -67,6 +68,30 @@ class TestComScoreSrg {
job.cancel()
}

@Test
fun testUserConsentGiven() {
val userConsent = ComScoreUserConsent.GIVEN
val expectedLabel = "1"
comScore.setUserConsent(userConsent)
Assert.assertEquals(expectedLabel, comScore.getPersistentLabel(ComScoreLabel.CS_UC_FR.label))
}

@Test
fun testUserConsentNotGiven() {
val userConsent = ComScoreUserConsent.REFUSED
val expectedLabel = "0"
comScore.setUserConsent(userConsent)
Assert.assertEquals(expectedLabel, comScore.getPersistentLabel(ComScoreLabel.CS_UC_FR.label))
}

@Test
fun testUserConsentUnknown() {
val userConsent = ComScoreUserConsent.UNKNOWN
val expectedLabel = ""
comScore.setUserConsent(userConsent)
Assert.assertEquals(expectedLabel, comScore.getPersistentLabel(ComScoreLabel.CS_UC_FR.label))
}

private class PageViewTracking : ComScoreSrg.DebugListener {
val pageViewFlow = MutableSharedFlow<Map<String, String>>(extraBufferCapacity = 1, replay = 1)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package ch.srgssr.pillarbox.analytics

import androidx.test.platform.app.InstrumentationRegistry
import ch.srgssr.pillarbox.analytics.commandersact.CommandersActEvent
import ch.srgssr.pillarbox.analytics.commandersact.CommandersActLabels
import ch.srgssr.pillarbox.analytics.commandersact.CommandersActPageView
import ch.srgssr.pillarbox.analytics.commandersact.CommandersActSrg
import org.junit.Assert
Expand Down Expand Up @@ -39,4 +40,12 @@ class TestCommandersAct {
)
Assert.assertTrue(true)
}

@Test
fun testConsentServices() {
val services = listOf("service1", "service2")
val expected = "service1,service2"
commandersAct.setConsentServices(services)
Assert.assertEquals(expected, commandersAct.getPermanentDataLabel(CommandersActLabels.CONSENT_SERVICES.label))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ package ch.srgssr.pillarbox.analytics
* @property sourceKey The CommandersAct sourceKey given by the analytics team.
* @property nonLocalizedApplicationName Application name for the analytics, by default the application name defined in the manifest. You can set
* it to null if the application name is not localized.
* @property userConsent User consent to transmit to ComScore and CommandersAct.
* @property comScorePersistentLabels initial ComScore persistent labels.
* @property commandersActPersistentLabels initial CommandersAct persistent labels.
*/
Expand All @@ -20,6 +21,7 @@ data class AnalyticsConfig(
val appSiteName: String,
val sourceKey: String,
val nonLocalizedApplicationName: String? = null,
val userConsent: UserConsent = UserConsent(),
val comScorePersistentLabels: Map<String, String>? = null,
val commandersActPersistentLabels: Map<String, String>? = null,
) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/*
* Copyright (c) 2023. SRG SSR. All rights reserved.
* License information is available from the LICENSE file.
*/
package ch.srgssr.pillarbox.analytics

import ch.srgssr.pillarbox.analytics.comscore.ComScoreUserConsent

/**
* User consent
*
* @property comScore ComScore user consent.
* @property commandersActConsentServices CommandersAct consent services list.
*/
data class UserConsent(
val comScore: ComScoreUserConsent = ComScoreUserConsent.UNKNOWN,
val commandersActConsentServices: List<String> = emptyList()
)
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,11 @@ interface CommandersAct {
* @return null if not found.
*/
fun getPermanentDataLabel(label: String): String?

/**
* Set consent services
*
* @param consentServices The list of consent services by the user.
*/
fun setConsentServices(consentServices: List<String>)
}
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,10 @@ internal class CommandersActSrg(
return tcServerSide.getPermanentData(label)
}

override fun setConsentServices(consentServices: List<String>) {
tcServerSide.addPermanentData(CommandersActLabels.CONSENT_SERVICES.label, consentServices.joinToString(","))
}

/**
* Override application name if [AnalyticsConfig.nonLocalizedApplicationName] is not empty.
* Useful for application that localized their application name and want to have same name for analytics.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,11 @@ interface ComScore {
* @return null if not found.
*/
fun getPersistentLabel(label: String): String?

/**
* Set user consent
*
* @param userConsent
*/
fun setUserConsent(userConsent: ComScoreUserConsent)
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,9 @@
*/
package ch.srgssr.pillarbox.analytics.comscore

/**
* Public ComScore label
*/
object ComScoreLabel {
/**
* Please refer to ComScore android implementation guide section 2.5.
*/
const val USER_CONSENT = "cs_ucfr"
internal enum class ComScoreLabel(val label: String) {
C8("c8"),
MP_BRAND("mp_brand"),
MP_V("mp_v"),
CS_UC_FR("cs_ucfr")
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ data class ComScorePageView(
fun toLabels(): Map<String, String> {
val labels = HashMap<String, String>()
labels.putAll(this.labels.filterValues { value -> value.isNotBlank() })
labels[ComScoreLabelInternal.C8.label] = name
labels[ComScoreLabel.C8.label] = name
return labels
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,15 @@ internal object ComScoreSrg : ComScore {
return this
}
this.config = config

val persistentLabels = HashMap<String, String>()
config.comScorePersistentLabels?.let { labels ->
persistentLabels.putAll(labels)
}

val userConsentLabel = getUserConsentPair(config.userConsent.comScore)
persistentLabels[userConsentLabel.first] = userConsentLabel.second

val versionName: String = try {
// When unit testing from library packageInfo.versionName is null!
context.applicationContext.packageManager.getPackageInfo(context.applicationContext.packageName, 0).versionName
Expand All @@ -66,8 +70,8 @@ internal object ComScoreSrg : ComScore {
Log.e("COMSCORE", "Cannot find package", e)
BuildConfig.VERSION_NAME
}
persistentLabels[ComScoreLabelInternal.MP_V.label] = versionName
persistentLabels[ComScoreLabelInternal.MP_BRAND.label] = config.vendor.toString()
persistentLabels[ComScoreLabel.MP_V.label] = versionName
persistentLabels[ComScoreLabel.MP_BRAND.label] = config.vendor.toString()
val publisher = PublisherConfiguration.Builder()
.publisherId(publisherId)
.persistentLabels(persistentLabels)
Expand Down Expand Up @@ -121,6 +125,23 @@ internal object ComScoreSrg : ComScore {
return configuration.getPersistentLabel(label)
}

override fun setUserConsent(userConsent: ComScoreUserConsent) {
putPersistentLabels(mapOf(getUserConsentPair(userConsent)))
// Analytics.notifyHiddenEvent() // FIXME Send updated user consent or wait next page view or media event?
}

/**
* Values from ComScore documentation section 2.5.2
*/
private fun getUserConsentPair(userConsent: ComScoreUserConsent): Pair<String, String> {
val value = when (userConsent) {
ComScoreUserConsent.GIVEN -> "1"
ComScoreUserConsent.REFUSED -> "0"
ComScoreUserConsent.UNKNOWN -> ""
}
return Pair(ComScoreLabel.CS_UC_FR.label, value)
}

private fun checkInitialized() {
requireNotNull(config) { "ComScore init has to be called before start." }
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Copyright (c) 2023. SRG SSR. All rights reserved.
* License information is available from the LICENSE file.
*/
package ch.srgssr.pillarbox.analytics.comscore

/**
* Com score user consent
*/
enum class ComScoreUserConsent {
/**
* Unknown
*
* User has not taken an action.
*/
UNKNOWN,

/**
* Given
*
* User has given consent.
*/
GIVEN,

/**
* Refuse
*
* User has not given consent or has opted out.
*/
REFUSED,
;
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
*/
package ch.srgssr.pillarbox.analytics

import ch.srgssr.pillarbox.analytics.comscore.ComScoreLabelInternal
import ch.srgssr.pillarbox.analytics.comscore.ComScoreLabel
import ch.srgssr.pillarbox.analytics.comscore.ComScorePageView
import org.junit.Assert
import org.junit.Test
Expand All @@ -17,7 +17,7 @@ class ComScoreEventTest {
val pageView = ComScorePageView(title)
val actual = pageView.toLabels()
val expected = HashMap<String, String>().apply {
this[ComScoreLabelInternal.C8.label] = "title 1"
this[ComScoreLabel.C8.label] = "title 1"
}
Assert.assertEquals(actual, expected)
}
Expand All @@ -36,7 +36,7 @@ class ComScoreEventTest {
val pageView = ComScorePageView(title, customLabels)
val actual = pageView.toLabels()
val expected = HashMap<String, String>().apply {
this[ComScoreLabelInternal.C8.label] = "title 1"
this[ComScoreLabel.C8.label] = "title 1"
this["key1"] = "value1"
}
Assert.assertEquals(actual, expected)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import ch.srgssr.pillarbox.analytics.commandersact.CommandersActPageView
import ch.srgssr.pillarbox.analytics.commandersact.TCMediaEvent
import ch.srgssr.pillarbox.analytics.comscore.ComScore
import ch.srgssr.pillarbox.analytics.comscore.ComScorePageView
import ch.srgssr.pillarbox.analytics.comscore.ComScoreUserConsent
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableSharedFlow
Expand Down Expand Up @@ -71,7 +72,7 @@ class TestSRGAnalyticsPageViews {
Assert.assertTrue(pageViewFlow.tryEmit(pageView))
}

override fun putPersistentLabels(labels: Map<String, String>?) {
override fun putPersistentLabels(labels: Map<String, String>) {
// Nothing
}

Expand All @@ -83,6 +84,10 @@ class TestSRGAnalyticsPageViews {
// Nothing
return null
}

override fun setUserConsent(userConsent: ComScoreUserConsent) {
// Nothing
}
}

private class DummyCommandersAct : CommandersAct {
Expand Down Expand Up @@ -113,5 +118,9 @@ class TestSRGAnalyticsPageViews {
return null
}

override fun setConsentServices(consentServices: List<String>) {
// Nothing
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ package ch.srgssr.pillarbox.demo
import android.app.Application
import ch.srgssr.pillarbox.analytics.AnalyticsConfig
import ch.srgssr.pillarbox.analytics.SRGAnalytics.initSRGAnalytics
import ch.srgssr.pillarbox.analytics.commandersact.CommandersActLabels
import ch.srgssr.pillarbox.analytics.comscore.ComScoreLabel
import ch.srgssr.pillarbox.analytics.UserConsent
import ch.srgssr.pillarbox.analytics.comscore.ComScoreUserConsent

/**
* Demo application
Expand All @@ -18,13 +18,17 @@ class DemoApplication : Application() {

override fun onCreate() {
super.onCreate()
// Defaults values
val initialUserConsent = UserConsent(
comScore = ComScoreUserConsent.UNKNOWN,
commandersActConsentServices = emptyList()
)
val config = AnalyticsConfig(
vendor = AnalyticsConfig.Vendor.SRG,
nonLocalizedApplicationName = "Pillarbox",
appSiteName = "pillarbox-demo-android",
sourceKey = AnalyticsConfig.SOURCE_KEY_SRG_DEBUG,
commandersActPersistentLabels = mapOf(Pair(CommandersActLabels.CONSENT_SERVICES.label, "service1,service2")),
comScorePersistentLabels = mapOf(Pair(ComScoreLabel.USER_CONSENT, ""))
userConsent = initialUserConsent
)
initSRGAnalytics(config = config)
}
Expand Down

0 comments on commit a98ee19

Please sign in to comment.