Skip to content

Commit 28f39e7

Browse files
authored
Merge pull request #5 from segment-integrations/sample_app
sample_app to main
2 parents c0a28e9 + 9246fa2 commit 28f39e7

File tree

11 files changed

+317
-16
lines changed

11 files changed

+317
-16
lines changed

testapp/build.gradle.kts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,13 @@ android {
88

99
defaultConfig {
1010
applicationId = "com.segment.analytics.destinations.mydestination.testapp"
11-
minSdk = 21
11+
minSdk = 23
1212
targetSdk = 31
1313
versionCode = 1
1414
versionName = "1.0"
1515

1616
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
17+
buildConfigField("String", "SEGMENT_WRITE_KEY", "\"YOUR_SEGMENT_WRITE_KEY\"")
1718
}
1819

1920
buildTypes {
@@ -44,4 +45,6 @@ dependencies {
4445
testImplementation("junit:junit:4.13.2")
4546
androidTestImplementation("androidx.test.ext:junit:1.1.3")
4647
androidTestImplementation("androidx.test.espresso:espresso-core:3.4.0")
48+
implementation("com.segment.analytics.kotlin:android:1.6.2")
49+
4750
}

testapp/src/main/AndroidManifest.xml

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,26 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
3-
package="com.segment.analytics.destinations.mydestination.testapp">
3+
package="com.segment.analytics.destinations.nielsendtvr.testapp">
4+
5+
<uses-permission android:name="android.permission.INTERNET" />
6+
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
47

58
<application
9+
android:name=".MainApplication"
610
android:allowBackup="true"
711
android:icon="@mipmap/ic_launcher"
812
android:label="@string/app_name"
913
android:roundIcon="@mipmap/ic_launcher_round"
1014
android:supportsRtl="true"
15+
android:usesCleartextTraffic="true"
1116
android:theme="@style/Theme.Analyticskotlindestinationtemplate">
17+
<activity
18+
android:name=".VideoActivity"
19+
android:exported="false">
20+
<meta-data
21+
android:name="android.app.lib_name"
22+
android:value="" />
23+
</activity>
1224
<activity
1325
android:name=".MainActivity"
1426
android:exported="true">

testapp/src/main/java/com/segment/analytics/destinations/mydestination/testapp/MainActivity.kt

Lines changed: 0 additions & 11 deletions
This file was deleted.
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
package com.segment.analytics.destinations.nielsendtvr.testapp
2+
3+
import com.segment.analytics.kotlin.core.Analytics
4+
import kotlinx.serialization.json.buildJsonObject
5+
import kotlinx.serialization.json.put
6+
7+
/**
8+
* Handler class to handle and call all Analytics track calls.
9+
*/
10+
class AnalyticsHandler(private val analytics: Analytics) {
11+
private fun trackEvent(event: String) {
12+
analytics.track(
13+
event,
14+
properties = buildJsonObject {
15+
put("channel", "A")
16+
}
17+
)
18+
}
19+
20+
fun trackContentStart() {
21+
analytics.track(
22+
"Video Content Started",
23+
properties = buildJsonObject {
24+
put("load_type", "linear")
25+
put("channel", "A")
26+
}
27+
)
28+
}
29+
30+
fun trackPlaybackResumed() {
31+
trackEvent("Video Playback Resumed")
32+
}
33+
34+
fun trackSeekCompleted() {
35+
trackEvent("Video Playback Seek Completed")
36+
}
37+
38+
fun trackBufferCompleted() {
39+
trackEvent("Video Playback Buffer Completed")
40+
}
41+
42+
fun trackPlaybackPaused() {
43+
analytics.track("Video Playback Paused")
44+
}
45+
46+
fun trackContentCompleted() {
47+
analytics.track("Video Content Completed")
48+
}
49+
50+
fun trackBufferStarted() {
51+
analytics.track("Video Playback Buffer Started")
52+
}
53+
54+
fun trackSeekStarted() {
55+
analytics.track("Video Playback Seek Started")
56+
}
57+
58+
fun trackApplicationBackgrounded() {
59+
analytics.track("Application Backgrounded")
60+
}
61+
62+
fun trackPlaybackCompleted() {
63+
analytics.track("Video Playback Completed")
64+
}
65+
66+
fun trackID3Event(id3: String) {
67+
analytics.track(
68+
"sendID3",
69+
properties = buildJsonObject {
70+
put("Id3", id3)
71+
}
72+
)
73+
}
74+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package com.segment.analytics.destinations.nielsendtvr.testapp
2+
3+
import android.content.Context
4+
import android.util.AttributeSet
5+
import android.widget.VideoView
6+
7+
/**
8+
* A Custom Video View to capture and control all video events by invoking Analytics specs.
9+
*/
10+
class AnalyticsVideoView : VideoView {
11+
constructor(context: Context?) : super(context)
12+
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)
13+
private var isResuming: Boolean= false
14+
private var analyticsHandler: AnalyticsHandler? = null
15+
16+
override fun start() {
17+
if (isResuming) {
18+
analyticsHandler?.trackPlaybackResumed()
19+
} else {
20+
analyticsHandler?.trackContentStart()
21+
isResuming = true
22+
}
23+
super.start()
24+
}
25+
26+
override fun pause() {
27+
analyticsHandler?.trackPlaybackPaused()
28+
super.pause()
29+
}
30+
31+
override fun seekTo(msec: Int) {
32+
analyticsHandler?.trackSeekStarted()
33+
super.seekTo(msec)
34+
}
35+
36+
fun setAnalytics(analyticsHandler: AnalyticsHandler) {
37+
this.analyticsHandler = analyticsHandler
38+
}
39+
40+
fun clearState() {
41+
isResuming = false
42+
}
43+
44+
fun setResuming() {
45+
isResuming = true
46+
}
47+
fun isResuming(): Boolean {
48+
return isResuming
49+
}
50+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package com.segment.analytics.destinations.nielsendtvr.testapp
2+
3+
import android.content.Intent
4+
import androidx.appcompat.app.AppCompatActivity
5+
import android.os.Bundle
6+
import android.widget.Button
7+
8+
class MainActivity : AppCompatActivity() {
9+
override fun onCreate(savedInstanceState: Bundle?) {
10+
super.onCreate(savedInstanceState)
11+
setContentView(R.layout.activity_main)
12+
findViewById<Button>(R.id.bt_play_video).setOnClickListener {
13+
startActivity(Intent(this@MainActivity, VideoActivity::class.java))
14+
}
15+
}
16+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package com.segment.analytics.destinations.nielsendtvr.testapp
2+
3+
import android.app.Application
4+
import com.segment.analytics.kotlin.android.Analytics
5+
import com.segment.analytics.kotlin.destinations.nielsendtvr.NielsenDTVRDestination
6+
7+
class MainApplication : Application() {
8+
companion object {
9+
lateinit var analyticsHandler: AnalyticsHandler
10+
}
11+
12+
override fun onCreate() {
13+
super.onCreate()
14+
val analytics = Analytics(BuildConfig.SEGMENT_WRITE_KEY, applicationContext) {
15+
this.collectDeviceId = true
16+
this.trackApplicationLifecycleEvents = true
17+
this.trackDeepLinks = true
18+
this.flushAt = 1
19+
this.flushInterval = 0
20+
}
21+
analytics.add(NielsenDTVRDestination())
22+
analyticsHandler = AnalyticsHandler(analytics)
23+
}
24+
25+
fun getAnalyticsHandler(): AnalyticsHandler {
26+
return analyticsHandler
27+
}
28+
}
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
package com.segment.analytics.destinations.nielsendtvr.testapp
2+
3+
import android.media.MediaPlayer
4+
import android.media.MediaPlayer.*
5+
import android.media.TimedMetaData
6+
import android.net.Uri
7+
import android.os.Bundle
8+
import android.widget.MediaController
9+
import androidx.appcompat.app.AppCompatActivity
10+
import java.nio.charset.StandardCharsets
11+
12+
class VideoActivity : AppCompatActivity(), OnPreparedListener, OnCompletionListener,
13+
OnInfoListener, OnTimedMetaDataAvailableListener, OnSeekCompleteListener {
14+
private lateinit var analyticsHandler: AnalyticsHandler
15+
private lateinit var videoView: AnalyticsVideoView
16+
private var savedProgress = 0
17+
private var isPlaying = true
18+
19+
override fun onCreate(savedInstanceState: Bundle?) {
20+
super.onCreate(savedInstanceState)
21+
setContentView(R.layout.activity_video)
22+
initAnalytics()
23+
initVideoView()
24+
}
25+
26+
private fun initAnalytics() {
27+
videoView = findViewById(R.id.cv_video)
28+
analyticsHandler = (application as MainApplication).getAnalyticsHandler()
29+
videoView.setAnalytics(analyticsHandler)
30+
}
31+
32+
private fun initVideoView() {
33+
val sampleVideoUrl = "http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4"
34+
videoView.setOnPreparedListener(this)
35+
videoView.setOnCompletionListener(this)
36+
videoView.setOnInfoListener(this)
37+
videoView.setVideoURI(Uri.parse(sampleVideoUrl))
38+
val mediaController = MediaController(this)
39+
mediaController.setAnchorView(videoView)
40+
videoView.setMediaController(mediaController)
41+
}
42+
43+
private fun saveProgress() {
44+
if (isFinishing) {
45+
analyticsHandler.trackPlaybackCompleted()
46+
} else {
47+
savedProgress = videoView.currentPosition
48+
isPlaying = videoView.isPlaying
49+
analyticsHandler.trackApplicationBackgrounded()
50+
}
51+
}
52+
53+
private fun resumeProgress(mp: MediaPlayer) {
54+
if (savedProgress > 0) {
55+
videoView.seekTo(savedProgress)
56+
}
57+
if (isPlaying) {
58+
if (videoView.isResuming()) {
59+
analyticsHandler.trackPlaybackResumed()
60+
} else {
61+
analyticsHandler.trackContentStart()
62+
videoView.setResuming()
63+
}
64+
mp.start()
65+
}
66+
}
67+
override fun onPause() {
68+
super.onPause()
69+
saveProgress()
70+
}
71+
72+
override fun onPrepared(mp: MediaPlayer) {
73+
mp.setOnSeekCompleteListener(this)
74+
mp.setOnTimedMetaDataAvailableListener(this)
75+
resumeProgress(mp)
76+
}
77+
78+
override fun onInfo(mp: MediaPlayer?, what: Int, extra: Int): Boolean {
79+
when (what) {
80+
MEDIA_INFO_BUFFERING_START -> analyticsHandler.trackBufferStarted()
81+
MEDIA_INFO_BUFFERING_END -> analyticsHandler.trackBufferCompleted()
82+
}
83+
return false
84+
}
85+
86+
override fun onCompletion(mp: MediaPlayer?) {
87+
analyticsHandler.trackContentCompleted()
88+
analyticsHandler.trackPlaybackCompleted()
89+
videoView.clearState()
90+
}
91+
92+
override fun onSeekComplete(mp: MediaPlayer?) {
93+
analyticsHandler.trackSeekCompleted()
94+
}
95+
96+
override fun onTimedMetaDataAvailable(mp: MediaPlayer, data: TimedMetaData?) {
97+
if (data != null && mp.isPlaying) {
98+
val metadata = data.metaData
99+
if (metadata != null) {
100+
val iD3Payload = String(metadata, StandardCharsets.UTF_8)
101+
val index = iD3Payload.indexOf("www.nielsen.com")
102+
if (index != -1) {
103+
val id3String = iD3Payload.substring(index, index + 249)
104+
analyticsHandler.trackID3Event(id3String)
105+
}
106+
}
107+
}
108+
}
109+
}

testapp/src/main/res/layout/activity_main.xml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,16 @@
66
android:layout_height="match_parent"
77
tools:context=".MainActivity">
88

9-
<TextView
9+
<Button
10+
android:id="@+id/bt_play_video"
1011
android:layout_width="wrap_content"
1112
android:layout_height="wrap_content"
12-
android:text="Hello World!"
13+
android:text="@string/play_video"
1314
app:layout_constraintBottom_toBottomOf="parent"
1415
app:layout_constraintLeft_toLeftOf="parent"
1516
app:layout_constraintRight_toRightOf="parent"
1617
app:layout_constraintTop_toTopOf="parent" />
1718

19+
20+
1821
</androidx.constraintlayout.widget.ConstraintLayout>
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
3+
xmlns:app="http://schemas.android.com/apk/res-auto"
4+
xmlns:tools="http://schemas.android.com/tools"
5+
android:layout_width="match_parent"
6+
android:layout_height="match_parent"
7+
tools:context="com.segment.analytics.destinations.nielsendtvr.testapp.VideoActivity">
8+
<com.segment.analytics.destinations.nielsendtvr.testapp.AnalyticsVideoView
9+
android:id="@+id/cv_video"
10+
android:layout_width="wrap_content"
11+
android:layout_height="wrap_content"
12+
app:layout_constraintBottom_toBottomOf="parent"
13+
app:layout_constraintLeft_toLeftOf="parent"
14+
app:layout_constraintRight_toRightOf="parent"
15+
app:layout_constraintTop_toTopOf="parent" />
16+
</androidx.constraintlayout.widget.ConstraintLayout>

0 commit comments

Comments
 (0)