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
45 changes: 29 additions & 16 deletions media/src/main/java/com/mparticle/media/MediaSession.kt
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ class MediaSession protected constructor(builder: Builder) {
val mediaContentTimeSpent: Double
get() { //total seconds spent playing content
return currentPlaybackStartTimestamp?.let {
this.storedPlaybackTime + (System.currentTimeMillis().minus(it) / 1000).toDouble()
this.storedPlaybackTime + (System.currentTimeMillis() - it) / 1000.0
} ?: this.storedPlaybackTime
}
var mediaContentCompleteLimit: Int = 100
Expand Down Expand Up @@ -119,6 +119,7 @@ class MediaSession protected constructor(builder: Builder) {
var storedPlaybackTime: Double = 0.0 //On Pause calculate playback time and clear currentPlaybackTime
private set
private var sessionSummarySent = false // Ensures we only send summary event once
private var playbackState: PlaybackState = PlaybackState.PAUSED_BY_USER // Tracks whether playback was playing, paused, or paused by ad break

private var testing = false // Enabled for test cases

Expand Down Expand Up @@ -191,6 +192,8 @@ class MediaSession protected constructor(builder: Builder) {
if (currentPlaybackStartTimestamp == null) {
currentPlaybackStartTimestamp = System.currentTimeMillis()
}

playbackState = PlaybackState.PLAYING
val playEvent = MediaEvent(this, MediaEventName.PLAY, options = options)
logEvent(playEvent)
}
Expand All @@ -203,6 +206,8 @@ class MediaSession protected constructor(builder: Builder) {
storedPlaybackTime += ((System.currentTimeMillis() - it) / 1000)
currentPlaybackStartTimestamp = null;
}

playbackState = PlaybackState.PAUSED_BY_USER
val pauseEvent = MediaEvent(this, MediaEventName.PAUSE, options = options)
logEvent(pauseEvent)
}
Expand Down Expand Up @@ -575,28 +580,30 @@ class MediaSession protected constructor(builder: Builder) {
}

private fun pauseContentTimeIfAdBreakExclusionEnabled() {
if (!excludeAdBreaksFromContentTime) {
currentPlaybackStartTimestamp?.let {
storedPlaybackTime += ((System.currentTimeMillis() - it) / 1000)
currentPlaybackStartTimestamp = null
}
if (!excludeAdBreaksFromContentTime || playbackState != PlaybackState.PLAYING) return

currentPlaybackStartTimestamp?.let {
storedPlaybackTime += (System.currentTimeMillis() - it) / 1000.0
currentPlaybackStartTimestamp = null
playbackState = PlaybackState.PAUSED_BY_AD_BREAK
}
}

private fun resumeContentTimeIfAdBreakExclusionEnabled() {
if (!excludeAdBreaksFromContentTime) {
if (currentPlaybackStartTimestamp != null) {
currentPlaybackStartTimestamp = System.currentTimeMillis()
}
}
if (!excludeAdBreaksFromContentTime || playbackState != PlaybackState.PAUSED_BY_AD_BREAK) return

currentPlaybackStartTimestamp = System.currentTimeMillis()
playbackState = PlaybackState.PLAYING
}

private fun logAdSummary(content: MediaAd?) {
content?.let { ad ->
ad.adStartTimestamp?.let { startTime ->
val endTime = System.currentTimeMillis()
ad.adEndTimestamp = endTime
mediaTotalAdTimeSpent += ((endTime - startTime) / 1000).toDouble()
val endTime = ad.adEndTimestamp ?: System.currentTimeMillis()
if (ad.adEndTimestamp == null) {
ad.adEndTimestamp = endTime
mediaTotalAdTimeSpent += ((endTime - startTime) / 1000).toDouble()
}
}

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

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

}

private enum class PlaybackState {
PLAYING,
PAUSED_BY_USER,
PAUSED_BY_AD_BREAK
}

private fun String?.require(variableName: String): String {
if (this == null) {
Logger.error("\"$variableName\" should not be null")
Expand Down
38 changes: 32 additions & 6 deletions media/src/test/java/com/mparticle/MediaSessionTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -679,7 +679,7 @@
}

@Test
fun testContentTimeExcludesAdBreak_When_Flag_Disable() {
fun testExcludeAdBreaksFromContentTime_Default_Disabled_IncludesAdTime() {

Check warning on line 682 in media/src/test/java/com/mparticle/MediaSessionTest.kt

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Rename function "testExcludeAdBreaksFromContentTime_Default_Disabled_IncludesAdTime" to match the regular expression ^[a-zA-Z][a-zA-Z0-9]*$

See more on https://sonarcloud.io/project/issues?id=mParticle_mparticle-android-media-sdk&issues=AZr-3Gr-qBhanUnLUzYV&open=AZr-3Gr-qBhanUnLUzYV&pullRequest=76
val mparticle = MockMParticle()
val events = mutableListOf<MediaEvent>()
val mediaSession = MediaSession.builder(mparticle) {
Expand All @@ -695,13 +695,13 @@
mediaSession.logAdBreakStart {
id = "break-1"
}
Thread.sleep(1000) // should NOT count toward content time
Thread.sleep(1000)
mediaSession.logPause()
mediaSession.logAdBreakEnd()


val contentTime = mediaSession.mediaContentTimeSpent
assertEquals(1.0, contentTime)
assertEquals(2.0, contentTime, 0.2)

val playCount = events.count { it.eventName == MediaEventName.PLAY }
val pauseCount = events.count { it.eventName == MediaEventName.PAUSE }
Expand All @@ -710,7 +710,7 @@
}

@Test
fun testDefaultBehavior_Unchanged_When_Flag_Enable() {
fun testExcludeAdBreaksFromContentTime_Enabled_ExcludesAdTime() {

Check warning on line 713 in media/src/test/java/com/mparticle/MediaSessionTest.kt

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Rename function "testExcludeAdBreaksFromContentTime_Enabled_ExcludesAdTime" to match the regular expression ^[a-zA-Z][a-zA-Z0-9]*$

See more on https://sonarcloud.io/project/issues?id=mParticle_mparticle-android-media-sdk&issues=AZr-3Gr-qBhanUnLUzYW&open=AZr-3Gr-qBhanUnLUzYW&pullRequest=76
val mparticle = MockMParticle()
val mediaSession = MediaSession.builder(mparticle) {
title = "hello"
Expand All @@ -724,12 +724,38 @@
mediaSession.logAdBreakStart {
id = "break-2"
}
Thread.sleep(1000) // should count toward content time when flag disabled
Thread.sleep(1000)
mediaSession.logAdBreakEnd()
mediaSession.logPause()

val contentTime = mediaSession.mediaContentTimeSpent
assertEquals(2.0, contentTime)
assertEquals(1.0, contentTime, 0.2)
}

@Test
fun testExcludeAdBreaksFromContentTime_UserPausesMidAd_ThenAdBreakEnds() {

Check warning on line 736 in media/src/test/java/com/mparticle/MediaSessionTest.kt

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Rename function "testExcludeAdBreaksFromContentTime_UserPausesMidAd_ThenAdBreakEnds" to match the regular expression ^[a-zA-Z][a-zA-Z0-9]*$

See more on https://sonarcloud.io/project/issues?id=mParticle_mparticle-android-media-sdk&issues=AZr-3Gr-qBhanUnLUzYX&open=AZr-3Gr-qBhanUnLUzYX&pullRequest=76
val mparticle = MockMParticle()
val mediaSession = MediaSession.builder(mparticle) {
title = "hello"
mediaContentId ="123"
duration =1000
excludeAdBreaksFromContentTime = true
}

mediaSession.logPlay()
Thread.sleep(1000)

mediaSession.logAdBreakStart {
id = "break-3"
}
Thread.sleep(1000)

mediaSession.logPause()

mediaSession.logAdBreakEnd()

val contentTime = mediaSession.mediaContentTimeSpent
assertEquals(1.0, contentTime, 0.2)
}
}

Expand Down