Skip to content

Commit 84b1493

Browse files
fix: adjust ad break exclusion logic (#76)
1 parent ac8de37 commit 84b1493

File tree

2 files changed

+61
-22
lines changed

2 files changed

+61
-22
lines changed

media/src/main/java/com/mparticle/media/MediaSession.kt

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ class MediaSession protected constructor(builder: Builder) {
9191
val mediaContentTimeSpent: Double
9292
get() { //total seconds spent playing content
9393
return currentPlaybackStartTimestamp?.let {
94-
this.storedPlaybackTime + (System.currentTimeMillis().minus(it) / 1000).toDouble()
94+
this.storedPlaybackTime + (System.currentTimeMillis() - it) / 1000.0
9595
} ?: this.storedPlaybackTime
9696
}
9797
var mediaContentCompleteLimit: Int = 100
@@ -119,6 +119,7 @@ class MediaSession protected constructor(builder: Builder) {
119119
var storedPlaybackTime: Double = 0.0 //On Pause calculate playback time and clear currentPlaybackTime
120120
private set
121121
private var sessionSummarySent = false // Ensures we only send summary event once
122+
private var playbackState: PlaybackState = PlaybackState.PAUSED_BY_USER // Tracks whether playback was playing, paused, or paused by ad break
122123

123124
private var testing = false // Enabled for test cases
124125

@@ -191,6 +192,8 @@ class MediaSession protected constructor(builder: Builder) {
191192
if (currentPlaybackStartTimestamp == null) {
192193
currentPlaybackStartTimestamp = System.currentTimeMillis()
193194
}
195+
196+
playbackState = PlaybackState.PLAYING
194197
val playEvent = MediaEvent(this, MediaEventName.PLAY, options = options)
195198
logEvent(playEvent)
196199
}
@@ -203,6 +206,8 @@ class MediaSession protected constructor(builder: Builder) {
203206
storedPlaybackTime += ((System.currentTimeMillis() - it) / 1000)
204207
currentPlaybackStartTimestamp = null;
205208
}
209+
210+
playbackState = PlaybackState.PAUSED_BY_USER
206211
val pauseEvent = MediaEvent(this, MediaEventName.PAUSE, options = options)
207212
logEvent(pauseEvent)
208213
}
@@ -575,28 +580,30 @@ class MediaSession protected constructor(builder: Builder) {
575580
}
576581

577582
private fun pauseContentTimeIfAdBreakExclusionEnabled() {
578-
if (!excludeAdBreaksFromContentTime) {
579-
currentPlaybackStartTimestamp?.let {
580-
storedPlaybackTime += ((System.currentTimeMillis() - it) / 1000)
581-
currentPlaybackStartTimestamp = null
582-
}
583+
if (!excludeAdBreaksFromContentTime || playbackState != PlaybackState.PLAYING) return
584+
585+
currentPlaybackStartTimestamp?.let {
586+
storedPlaybackTime += (System.currentTimeMillis() - it) / 1000.0
587+
currentPlaybackStartTimestamp = null
588+
playbackState = PlaybackState.PAUSED_BY_AD_BREAK
583589
}
584590
}
585591

586592
private fun resumeContentTimeIfAdBreakExclusionEnabled() {
587-
if (!excludeAdBreaksFromContentTime) {
588-
if (currentPlaybackStartTimestamp != null) {
589-
currentPlaybackStartTimestamp = System.currentTimeMillis()
590-
}
591-
}
593+
if (!excludeAdBreaksFromContentTime || playbackState != PlaybackState.PAUSED_BY_AD_BREAK) return
594+
595+
currentPlaybackStartTimestamp = System.currentTimeMillis()
596+
playbackState = PlaybackState.PLAYING
592597
}
593598

594599
private fun logAdSummary(content: MediaAd?) {
595600
content?.let { ad ->
596601
ad.adStartTimestamp?.let { startTime ->
597-
val endTime = System.currentTimeMillis()
598-
ad.adEndTimestamp = endTime
599-
mediaTotalAdTimeSpent += ((endTime - startTime) / 1000).toDouble()
602+
val endTime = ad.adEndTimestamp ?: System.currentTimeMillis()
603+
if (ad.adEndTimestamp == null) {
604+
ad.adEndTimestamp = endTime
605+
mediaTotalAdTimeSpent += ((endTime - startTime) / 1000).toDouble()
606+
}
600607
}
601608

602609
val customAttributes: MutableMap<String, String> = mutableMapOf()
@@ -777,10 +784,10 @@ class MediaSession protected constructor(builder: Builder) {
777784
}
778785

779786
/**
780-
* When enabled, automatically pauses content time tracking during ad breaks and resumes after.
787+
* When enabled, ad break time is excluded from content time tracking.
781788
* When disabled (default), ad break time is included in content time spent.
782789
*/
783-
fun pauseContentDuringAdBreaks(shouldPause: Boolean): Builder {
790+
fun excludeAdBreaksFromContentTime(shouldPause: Boolean): Builder {
784791
this.excludeAdBreaksFromContentTime = shouldPause
785792
return this
786793
}
@@ -795,6 +802,12 @@ class MediaSession protected constructor(builder: Builder) {
795802

796803
}
797804

805+
private enum class PlaybackState {
806+
PLAYING,
807+
PAUSED_BY_USER,
808+
PAUSED_BY_AD_BREAK
809+
}
810+
798811
private fun String?.require(variableName: String): String {
799812
if (this == null) {
800813
Logger.error("\"$variableName\" should not be null")

media/src/test/java/com/mparticle/MediaSessionTest.kt

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -679,7 +679,7 @@ class MediaSessionTest {
679679
}
680680

681681
@Test
682-
fun testContentTimeExcludesAdBreak_When_Flag_Disable() {
682+
fun testExcludeAdBreaksFromContentTime_Default_Disabled_IncludesAdTime() {
683683
val mparticle = MockMParticle()
684684
val events = mutableListOf<MediaEvent>()
685685
val mediaSession = MediaSession.builder(mparticle) {
@@ -695,13 +695,13 @@ class MediaSessionTest {
695695
mediaSession.logAdBreakStart {
696696
id = "break-1"
697697
}
698-
Thread.sleep(1000) // should NOT count toward content time
698+
Thread.sleep(1000)
699699
mediaSession.logPause()
700700
mediaSession.logAdBreakEnd()
701701

702702

703703
val contentTime = mediaSession.mediaContentTimeSpent
704-
assertEquals(1.0, contentTime)
704+
assertEquals(2.0, contentTime, 0.2)
705705

706706
val playCount = events.count { it.eventName == MediaEventName.PLAY }
707707
val pauseCount = events.count { it.eventName == MediaEventName.PAUSE }
@@ -710,7 +710,7 @@ class MediaSessionTest {
710710
}
711711

712712
@Test
713-
fun testDefaultBehavior_Unchanged_When_Flag_Enable() {
713+
fun testExcludeAdBreaksFromContentTime_Enabled_ExcludesAdTime() {
714714
val mparticle = MockMParticle()
715715
val mediaSession = MediaSession.builder(mparticle) {
716716
title = "hello"
@@ -724,12 +724,38 @@ class MediaSessionTest {
724724
mediaSession.logAdBreakStart {
725725
id = "break-2"
726726
}
727-
Thread.sleep(1000) // should count toward content time when flag disabled
727+
Thread.sleep(1000)
728728
mediaSession.logAdBreakEnd()
729729
mediaSession.logPause()
730730

731731
val contentTime = mediaSession.mediaContentTimeSpent
732-
assertEquals(2.0, contentTime)
732+
assertEquals(1.0, contentTime, 0.2)
733+
}
734+
735+
@Test
736+
fun testExcludeAdBreaksFromContentTime_UserPausesMidAd_ThenAdBreakEnds() {
737+
val mparticle = MockMParticle()
738+
val mediaSession = MediaSession.builder(mparticle) {
739+
title = "hello"
740+
mediaContentId ="123"
741+
duration =1000
742+
excludeAdBreaksFromContentTime = true
743+
}
744+
745+
mediaSession.logPlay()
746+
Thread.sleep(1000)
747+
748+
mediaSession.logAdBreakStart {
749+
id = "break-3"
750+
}
751+
Thread.sleep(1000)
752+
753+
mediaSession.logPause()
754+
755+
mediaSession.logAdBreakEnd()
756+
757+
val contentTime = mediaSession.mediaContentTimeSpent
758+
assertEquals(1.0, contentTime, 0.2)
733759
}
734760
}
735761

0 commit comments

Comments
 (0)