Skip to content

Commit 5371642

Browse files
committed
feat(#82): allow duration tracking including background time
1 parent aa755bb commit 5371642

File tree

5 files changed

+86
-30
lines changed

5 files changed

+86
-30
lines changed

lib/src/androidTest/java/com/telemetrydeck/sdk/DurationSignalTrackerProviderTest.kt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ class DurationSignalTrackerProviderTest {
3939
@Test
4040
fun tracks_a_signal_duration() {
4141
val sut = createSut()
42-
sut.startTracking("signal1", emptyMap())
42+
sut.startTracking("signal1", emptyMap(), false)
4343
// sleep for 5 seconds
4444
Thread.sleep(5000)
4545
val params = sut.stopTracking("signal1", emptyMap())
@@ -60,7 +60,7 @@ class DurationSignalTrackerProviderTest {
6060
@Test
6161
fun tracks_a_signal_duration_with_starting_and_endingparams() {
6262
val sut = createSut()
63-
sut.startTracking("signal1", mapOf("param1" to "value1"))
63+
sut.startTracking("signal1", mapOf("param1" to "value1"), false)
6464
// sleep for 5 seconds
6565
Thread.sleep(5000)
6666
val params = sut.stopTracking("signal1", mapOf("param2" to "value3"))
@@ -86,7 +86,7 @@ class DurationSignalTrackerProviderTest {
8686
val twoHoursAgo = Date(now.time - 7200000)
8787
val hourAgo = Date(now.time - 3600000)
8888
val state = DurationSignalTrackerProvider.TrackerState(
89-
signals = mapOf("signal1" to DurationSignalTrackerProvider.CachedData(startTime = twoHoursAgo, parameters = mapOf("param1" to "value1"))),
89+
signals = mapOf("signal1" to DurationSignalTrackerProvider.CachedData(startTime = twoHoursAgo, parameters = mapOf("param1" to "value1"), false)),
9090
lastEnteredBackground = hourAgo
9191
)
9292
prepareState(state)
@@ -114,13 +114,13 @@ class DurationSignalTrackerProviderTest {
114114
val twoHoursAgo = Date(now.time - 7200000)
115115
val hourAgo = Date(now.time - 3700000)
116116
val state = DurationSignalTrackerProvider.TrackerState(
117-
signals = mapOf("signal1" to DurationSignalTrackerProvider.CachedData(startTime = twoHoursAgo, parameters = mapOf("param1" to "value1"))),
117+
signals = mapOf("signal1" to DurationSignalTrackerProvider.CachedData(startTime = twoHoursAgo, parameters = mapOf("param1" to "value1"), false)),
118118
lastEnteredBackground = hourAgo
119119
)
120120
prepareState(state)
121121
val sut = createSut()
122122

123-
sut.handleOnStart()
123+
sut.handleOnForeground()
124124
val params = sut.stopTracking("signal1", mapOf("param2" to "value3"))
125125

126126
assertEquals("value1", params?.get("param1"))

lib/src/androidTest/java/com/telemetrydeck/sdk/TelemetryDeckTest.kt

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import androidx.lifecycle.LifecycleOwner
77
import androidx.test.annotation.UiThreadTest
88
import androidx.test.core.app.ApplicationProvider
99
import androidx.test.ext.junit.runners.AndroidJUnit4
10+
import com.telemetrydeck.sdk.providers.DurationSignalTrackerProvider
1011
import com.telemetrydeck.sdk.providers.SessionTrackingSignalProvider
1112
import com.telemetrydeck.sdk.signals.Acquisition
1213
import com.telemetrydeck.sdk.signals.Session
@@ -24,6 +25,8 @@ import java.io.File
2425
import java.net.URL
2526
import java.util.Date
2627
import java.util.UUID
28+
import kotlin.collections.find
29+
import kotlin.text.startsWith
2730

2831

2932
@RunWith(AndroidJUnit4::class)
@@ -334,11 +337,55 @@ class TelemetryDeckTest {
334337
it.payload.find { it.startsWith("TelemetryDeck.Signal.durationInSeconds:") }
335338
assertNotNull(duration)
336339
assertEquals(10.0, it.floatValue)
337-
assertEquals("04f8996da763b7a969b1028ee3007569eaf3a635486ddab211d512c85b9df8fb", it.clientUser)
340+
assertEquals(
341+
"04f8996da763b7a969b1028ee3007569eaf3a635486ddab211d512c85b9df8fb",
342+
it.clientUser
343+
)
338344
})
339345
}
340346
}
341347

348+
@UiThreadTest
349+
@Test
350+
fun duration_tracking_with_and_without_background_time() {
351+
startTelemetryDeck(
352+
prepareBuilder()
353+
.testMode(false)
354+
)
355+
356+
val sut =
357+
(TelemetryDeck.instance!!.providers.find { it is DurationSignalTrackerProvider } as DurationSignalTrackerProvider)
358+
359+
val now = Date()
360+
sut.handleOnForeground(now)
361+
362+
sut.startTracking("foreground", emptyMap(), includeBackgroundTime = false, now)
363+
sut.startTracking("foreground_and_background", emptyMap(), includeBackgroundTime = true, now)
364+
365+
// +600seconds, simulate going into the background 10 minutes later
366+
val time1 = Date(now.time + 1000 * 10 * 60)
367+
sut.handleOnBackground(
368+
Date(now.time + 1000 * 10 * 60)
369+
)
370+
371+
// +3600seconds, and back to foreground an hour later
372+
val endTime = Date(time1.time + (1000 * 60 * 60))
373+
374+
sut.handleOnForeground(
375+
endTime
376+
)
377+
378+
val paramsForeground = sut.stopTracking("foreground", emptyMap(), endTime)
379+
val paramsForeAndBackground =
380+
sut.stopTracking("foreground_and_background", emptyMap(), endTime)
381+
382+
assertNotNull(paramsForeground)
383+
assertNotNull(paramsForeAndBackground)
384+
385+
assertEquals("600.000", paramsForeground?.get("TelemetryDeck.Signal.durationInSeconds"))
386+
assertEquals("4200.000", paramsForeAndBackground?.get("TelemetryDeck.Signal.durationInSeconds"))
387+
}
388+
342389

343390
private fun prepareBuilder(): TelemetryDeck.Builder {
344391
val httpClient = mockk<TelemetryApiClient>()

lib/src/main/java/com/telemetrydeck/sdk/TelemetryDeck.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -155,13 +155,13 @@ class TelemetryDeck(
155155
)
156156
}
157157

158-
override fun startDurationSignal(signalName: String, parameters: Map<String, String>) {
158+
override fun startDurationSignal(signalName: String, parameters: Map<String, String>, includeBackgroundTime: Boolean) {
159159
val trackingProvider = this.providers.find { it is DurationSignalTrackerProvider } as? DurationSignalTrackerProvider
160160
if (trackingProvider == null) {
161161
this.logger?.error("startDurationSignal requires the DurationSignalTrackerProvider to be registered")
162162
return
163163
}
164-
trackingProvider.startTracking(signalName, parameters)
164+
trackingProvider.startTracking(signalName, parameters, includeBackgroundTime)
165165
}
166166

167167
override fun stopAndSendDurationSignal(signalName: String, parameters: Map<String, String>, floatValue: Double?, customUserID: String?) {
@@ -427,8 +427,8 @@ class TelemetryDeck(
427427
)
428428
}
429429

430-
override fun startDurationSignal(signalName: String, parameters: Map<String, String>) {
431-
getInstance()?.startDurationSignal(signalName, parameters)
430+
override fun startDurationSignal(signalName: String, parameters: Map<String, String>, includeBackgroundTime: Boolean) {
431+
getInstance()?.startDurationSignal(signalName, parameters, includeBackgroundTime)
432432
}
433433

434434
override fun stopAndSendDurationSignal(

lib/src/main/java/com/telemetrydeck/sdk/TelemetryDeckClient.kt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,8 +121,13 @@ interface TelemetryDeckClient {
121121
*
122122
* @param signalName The name of the signal to track. This will be used to identify and stop the duration tracking later.
123123
* @param parameters A dictionary of additional string key-value pairs that will be included when the duration signal is eventually sent.
124+
* @param includeBackgroundTime Indicates if the duration tracked will include the time spent in the background.
124125
*/
125-
fun startDurationSignal(signalName: String, parameters: Map<String, String> = emptyMap())
126+
fun startDurationSignal(
127+
signalName: String,
128+
parameters: Map<String, String> = emptyMap(),
129+
includeBackgroundTime: Boolean = false
130+
)
126131

127132
/** Stops tracking the duration of a signal and sends it with the total duration.
128133
*

lib/src/main/java/com/telemetrydeck/sdk/providers/DurationSignalTrackerProvider.kt

Lines changed: 23 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -38,14 +38,14 @@ class DurationSignalTrackerProvider : TelemetryDeckProvider, DefaultLifecycleObs
3838
}
3939

4040
override fun onStart(owner: LifecycleOwner) {
41-
handleOnStart()
41+
handleOnForeground()
4242
}
4343

4444
override fun onStop(owner: LifecycleOwner) {
45-
handleOnStop()
45+
handleOnBackground()
4646
}
4747

48-
fun handleOnStart() {
48+
fun handleOnForeground(now: Date = Date()) {
4949
this.manager?.get()?.debugLogger?.debug("Commencing signal duration tracking")
5050

5151
val preRestoreState = this.state
@@ -65,14 +65,17 @@ class DurationSignalTrackerProvider : TelemetryDeckProvider, DefaultLifecycleObs
6565
val lastEnteredBackground = currentState.lastEnteredBackground
6666
if (lastEnteredBackground != null) {
6767
// subtracts background time from all signals by moving their start time forward
68-
val now = Date()
6968
val backgroundDuration = now.time - lastEnteredBackground.time
7069
this.manager?.get()?.debugLogger?.debug("Adapting signal duration with -$backgroundDuration ms")
7170
currentState = currentState.copy(
7271
signals = currentState.signals.mapValues {
73-
it.value.copy(
74-
startTime = Date(it.value.startTime.time + backgroundDuration)
75-
)
72+
if (it.value.includeBackgroundTime == true) {
73+
it.value.copy()
74+
} else {
75+
it.value.copy(
76+
startTime = Date(it.value.startTime.time + backgroundDuration)
77+
)
78+
}
7679
}
7780
)
7881
}
@@ -81,31 +84,31 @@ class DurationSignalTrackerProvider : TelemetryDeckProvider, DefaultLifecycleObs
8184
this.state = currentState
8285
}
8386

84-
fun handleOnStop() {
87+
fun handleOnBackground(now: Date = Date()) {
8588
this.manager?.get()?.debugLogger?.debug("Signal duration tracking is shutting down")
86-
val currentState = this.state
89+
val currentState = this.state?.copy(
90+
lastEnteredBackground = now
91+
)
8792
if (currentState != null) {
88-
val updatedState = currentState.copy(
89-
lastEnteredBackground = Date()
90-
)
91-
writeStateToDisk(updatedState, this.appContext?.get(), this.fileName, this.fileEncoding, this.manager?.get()?.debugLogger)
93+
this.state = currentState
94+
writeStateToDisk(currentState, this.appContext?.get(), this.fileName, this.fileEncoding, this.manager?.get()?.debugLogger)
9295
}
9396
}
9497

9598
@Synchronized
96-
fun startTracking(signalName: String, parameters: Map<String, String>) {
99+
fun startTracking(signalName: String, parameters: Map<String, String>, includeBackgroundTime: Boolean, now: Date = Date()) {
97100
val currentState = state ?: throw Exception("startTracking called before register")
98-
val now = Date()
99101
this.state = currentState.copy(
100102
signals = currentState.signals + (signalName to CachedData(
101103
now,
102-
parameters
104+
parameters,
105+
includeBackgroundTime
103106
))
104107
)
105108
}
106109

107110
@Synchronized
108-
fun stopTracking(signalName: String, parameters: Map<String, String>): Map<String, String>? {
111+
fun stopTracking(signalName: String, parameters: Map<String, String>, now: Date = Date()): Map<String, String>? {
109112
val currentState = state ?: throw Exception("stopTracking called before register")
110113
val signalTracking = currentState.signals[signalName]
111114
if (signalTracking == null) {
@@ -116,7 +119,7 @@ class DurationSignalTrackerProvider : TelemetryDeckProvider, DefaultLifecycleObs
116119
this.state = currentState.copy(signals = currentState.signals - signalName)
117120

118121
// queue a duration signal for sending
119-
val trackingDurationMs = Date().time - signalTracking.startTime.time
122+
val trackingDurationMs = now.time - signalTracking.startTime.time
120123
val trackingDurationSec = trackingDurationMs / 1000.0
121124

122125
val mergedParameters = mutableMapOf<String, String>()
@@ -145,6 +148,7 @@ class DurationSignalTrackerProvider : TelemetryDeckProvider, DefaultLifecycleObs
145148
data class CachedData(
146149
@Serializable(with = DateSerializer::class)
147150
val startTime: Date,
148-
val parameters: Map<String, String>
151+
val parameters: Map<String, String>,
152+
val includeBackgroundTime: Boolean?
149153
)
150154
}

0 commit comments

Comments
 (0)