Skip to content

Commit 2db3946

Browse files
authored
Merge pull request #3 from segment-integrations/development
development to main
2 parents b1dc503 + 2af9e63 commit 2db3946

File tree

3 files changed

+227
-5
lines changed

3 files changed

+227
-5
lines changed

build.gradle.kts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ allprojects {
3333
mavenCentral()
3434
google()
3535
gradlePluginPortal()
36+
maven {
37+
url = uri("https://raw.githubusercontent.com/NielsenDigitalSDK/nielsenappsdk-android/master/")
38+
}
3639
}
3740
}
3841

lib/build.gradle.kts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ android {
4242
dependencies {
4343
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:1.1.5")
4444

45-
implementation("com.segment.analytics.kotlin:android:1.5.0")
45+
implementation("com.segment.analytics.kotlin:android:1.6.2")
4646
implementation("androidx.multidex:multidex:2.0.1")
4747
implementation("androidx.core:core-ktx:1.7.0")
4848

@@ -52,7 +52,7 @@ dependencies {
5252

5353
// Partner Dependencies
5454
dependencies {
55-
// TODO add your partner deps here
55+
implementation("com.nielsenappsdk:globalx:9.0.0.0")
5656
}
5757

5858
// Test Dependencies
Lines changed: 222 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,238 @@
11
package com.segment.analytics.kotlin.destinations.nielsendtvr
22

3+
import android.content.Context
4+
import com.nielsen.app.sdk.AppSdk
35
import com.segment.analytics.kotlin.core.*
46
import com.segment.analytics.kotlin.core.platform.DestinationPlugin
57
import com.segment.analytics.kotlin.core.platform.Plugin
8+
import com.segment.analytics.kotlin.core.platform.plugins.logger.log
9+
import com.segment.analytics.kotlin.core.utilities.toContent
10+
import kotlinx.serialization.Serializable
11+
import kotlinx.serialization.json.JsonObject
12+
import org.json.JSONException
13+
import org.json.JSONObject
14+
import java.util.*
615

716
class NielsenDTVRDestination : DestinationPlugin() {
8-
override val key: String = "Nielsen DTVR"
17+
companion object {
18+
private const val NIELSEN_DCR_FULL_KEY = "Nielsen DTVR"
19+
}
20+
21+
private var previousID3: String = ""
22+
internal var nielsenDTVRSettings: NielsenDTVRSettings? = null
23+
internal lateinit var appSdk: AppSdk
24+
internal var id3EventNames: ArrayList<String> = arrayListOf()
25+
private var id3PropertyName: String = ""
26+
27+
override val key: String = NIELSEN_DCR_FULL_KEY
928

1029
override fun update(settings: Settings, type: Plugin.UpdateType) {
1130
super.update(settings, type)
31+
this.nielsenDTVRSettings =
32+
settings.destinationSettings(key, NielsenDTVRSettings.serializer())
1233
if (type == Plugin.UpdateType.Initial) {
34+
if (nielsenDTVRSettings != null) {
35+
setupNielsenAppSdk()
36+
id3EventNames = parseId3EventNames()
37+
id3PropertyName = nielsenDTVRSettings!!.id3Property.ifEmpty { "id3" }
38+
}
39+
}
40+
}
41+
42+
override fun track(payload: TrackEvent): BaseEvent {
43+
if(!EventVideoEnum.isVideoEvent(payload.event)) {
44+
analytics.log("Event is not Video")
45+
return payload
46+
}
47+
val nielsenProperties: Map<String, String> = payload.properties.asStringMap()
48+
when (EventVideoEnum[payload.event]) {
49+
EventVideoEnum.ContentStarted -> {
50+
play(nielsenProperties)
51+
loadMetadata(nielsenProperties)
52+
}
53+
EventVideoEnum.PlaybackResumed,
54+
EventVideoEnum.PlaybackSeekCompleted,
55+
EventVideoEnum.PlaybackBufferCompleted -> {
56+
play(nielsenProperties)
57+
}
58+
EventVideoEnum.PlaybackPaused,
59+
EventVideoEnum.PlaybackInterrupted,
60+
EventVideoEnum.ContentCompleted,
61+
EventVideoEnum.PlaybackBufferStarted,
62+
EventVideoEnum.PlaybackSeekStarted,
63+
// Nielsen requested Video Playback Completed and new Video Playback Exited event map to stop as end is not used for DTVR
64+
EventVideoEnum.PlaybackExited,
65+
EventVideoEnum.PlaybackCompleted -> {
66+
stop()
67+
}
68+
EventVideoEnum.ApplicationBackgrounded -> {
69+
stop()
70+
}
71+
else -> {
72+
analytics.log("Video Event not found")
73+
}
74+
}
75+
if (id3EventNames.contains(payload.event.lowercase(Locale.getDefault()))) {
76+
sendID3(nielsenProperties)
77+
}
78+
return payload
79+
}
80+
81+
/**
82+
* creating Nielsen App SDK.
83+
*/
84+
private fun setupNielsenAppSdk() {
85+
var sfcode = "us"
86+
if (!nielsenDTVRSettings!!.sfCode.isNullOrEmpty()) {
87+
sfcode = nielsenDTVRSettings!!.sfCode!!
88+
}
89+
// Create AppSdk configuration object (JSONObject)
90+
val appSdkConfig: JSONObject = JSONObject()
91+
.put("appid", nielsenDTVRSettings!!.appId)
92+
.put("sfcode", sfcode)
93+
if (nielsenDTVRSettings!!.debug) {
94+
appSdkConfig.put("nol_devDebug", "DEBUG")
95+
}
96+
appSdk = AppSdk(analytics.configuration.application as Context, appSdkConfig, null)
97+
analytics.log("new AppSdk(${appSdkConfig.toString(2)})")
98+
}
99+
100+
/**
101+
* retrieves lowercase list of id3 event names from settings
102+
*
103+
* @return list of lower case id3 event names
104+
*/
105+
private fun parseId3EventNames(): ArrayList<String> {
106+
val id3EventNames = ArrayList<String>()
107+
id3EventNames.addAll(nielsenDTVRSettings!!.sendId3Events)
108+
for (i in id3EventNames.indices) {
109+
id3EventNames[i] = id3EventNames[i].lowercase(Locale.getDefault())
110+
}
111+
return id3EventNames
112+
}
113+
114+
/**
115+
* Creating Metadata in JSONObject type. JSON value must be string value. And send it to neilsen SDK
116+
* @param nielsenProperties Map<String, String> properties from payload of the Segment track event
117+
*/
118+
private fun loadMetadata(nielsenProperties: Map<String, String>) {
119+
val jsonMetadata = JSONObject()
120+
try {
121+
jsonMetadata.put("type", "content")
122+
if (nielsenProperties.containsKey("channel")) {
123+
jsonMetadata.put("channelName", nielsenProperties["channel"])
124+
}
125+
var loadType = ""
126+
if (nielsenProperties.containsKey("load_type")) {
127+
loadType = "load_type"
128+
}
129+
if (nielsenProperties.containsKey("loadType")) {
130+
loadType = "loadType"
131+
}
132+
if (loadType.isNotEmpty()) {
133+
jsonMetadata.put(
134+
"adModel",
135+
if (nielsenProperties[loadType].equals("dynamic")) "2" else "1"
136+
)
137+
}
138+
} catch (e: JSONException) {
139+
analytics.log("Failed to send loadMetadata event : $e")
140+
}
141+
analytics.log("appSdk.loadMetadata($jsonMetadata)")
142+
appSdk.loadMetadata(jsonMetadata)
143+
}
144+
145+
/**
146+
* a method to sendID3 value to NeilsenSDK
147+
* @param nielsenProperties Map<String, String> properties from payload of the Segment track event
148+
*/
149+
private fun sendID3(nielsenProperties: Map<String, String>) {
150+
val id3: String? = nielsenProperties[id3PropertyName]
151+
if (id3.isNullOrEmpty() || previousID3 == id3)
152+
return
153+
previousID3 = id3
154+
analytics.log("appSdk.sendID3$id3)")
155+
appSdk.sendID3(id3)
156+
}
157+
158+
private fun play(nielsenProperties: Map<String, String>) {
159+
val channelInfo = JSONObject()
160+
try {
161+
if (nielsenProperties.containsKey("channel")) {
162+
channelInfo.put("channelName", nielsenProperties["channel"])
163+
}
164+
} catch (e: JSONException) {
165+
analytics.log( "Failed to send play event : $e")
13166
}
167+
analytics.log("appSdk.play($channelInfo)")
168+
appSdk.play(channelInfo)
169+
}
170+
171+
private fun stop() {
172+
analytics.log("appSdk.stop()")
173+
appSdk.stop()
14174
}
175+
}
176+
177+
internal enum class EventVideoEnum(
178+
/**
179+
* Retrieves the Neilsen DCR video event name. This is different from `enum.name()`
180+
*
181+
* @return Event name.
182+
*/
183+
val eventName: String
184+
) {
185+
PlaybackPaused("Video Playback Paused"),
186+
PlaybackResumed("Video Playback Resumed"),
187+
PlaybackExited("Video Playback Exited"),
188+
PlaybackInterrupted("Video Playback Interrupted"),
189+
PlaybackCompleted("Video Playback Completed"),
190+
ContentStarted("Video Content Started"),
191+
ContentCompleted("Video Content Completed"),
192+
PlaybackBufferStarted("Video Playback Buffer Started"),
193+
PlaybackBufferCompleted("Video Playback Buffer Completed"),
194+
PlaybackSeekStarted("Video Playback Seek Started"),
195+
PlaybackSeekCompleted("Video Playback Seek Completed"),
196+
ApplicationBackgrounded("Application Backgrounded");
197+
198+
companion object {
199+
private var names: MutableMap<String, EventVideoEnum>? = null
200+
201+
init {
202+
names = HashMap()
203+
for (e in values()) {
204+
(names as HashMap<String, EventVideoEnum>)[e.eventName] = e
205+
}
206+
}
15207

16-
override fun track(payload: TrackEvent): BaseEvent? {
17-
return super.track(payload)
208+
operator fun get(name: String): EventVideoEnum? {
209+
if (names!!.containsKey(name)) {
210+
return names!![name]
211+
}
212+
throw IllegalArgumentException("$name is not a valid video event")
213+
}
214+
/**
215+
* Identifies if the event is a video event.
216+
*
217+
* @param eventName Event name
218+
* @return `true` if it's a video event, `false` otherwise.
219+
*/
220+
fun isVideoEvent(eventName: String): Boolean {
221+
return names!!.containsKey(eventName)
222+
}
18223
}
224+
}
225+
/**
226+
* NielsenDTVR Settings data class.
227+
*/
228+
@Serializable
229+
internal data class NielsenDTVRSettings(
230+
var appId: String,
231+
var debug: Boolean,
232+
var sfCode: String?,
233+
var sendId3Events: ArrayList<String> = arrayListOf(),
234+
var id3Property: String = "id3")
235+
236+
private fun JsonObject.asStringMap(): Map<String, String> = this.mapValues { (_, value) ->
237+
value.toContent().toString()
19238
}

0 commit comments

Comments
 (0)