-
Notifications
You must be signed in to change notification settings - Fork 61
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
RUM-7171 Add basic logic for interaction-to-next-view metric
- Loading branch information
Showing
17 changed files
with
1,036 additions
and
26 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
25 changes: 25 additions & 0 deletions
25
...tadog/android/rum/internal/metric/interactiontonextview/ActionTypeInteractionValidator.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
/* | ||
* Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. | ||
* This product includes software developed at Datadog (https://www.datadoghq.com/). | ||
* Copyright 2016-Present Datadog, Inc. | ||
*/ | ||
|
||
package com.datadog.android.rum.internal.metric.interactiontonextview | ||
|
||
import com.datadog.android.rum.RumActionType | ||
|
||
internal class ActionTypeInteractionValidator : InteractionIngestionValidator { | ||
override fun validate( | ||
context: InternalInteractionContext | ||
): Boolean { | ||
return context.actionType in ALLOWED_TYPES | ||
} | ||
|
||
companion object { | ||
private val ALLOWED_TYPES = setOf( | ||
RumActionType.TAP, | ||
RumActionType.SWIPE, | ||
RumActionType.CLICK | ||
) | ||
} | ||
} |
16 changes: 16 additions & 0 deletions
16
...atadog/android/rum/internal/metric/interactiontonextview/InteractionIngestionValidator.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
/* | ||
* Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. | ||
* This product includes software developed at Datadog (https://www.datadoghq.com/). | ||
* Copyright 2016-Present Datadog, Inc. | ||
*/ | ||
|
||
package com.datadog.android.rum.internal.metric.interactiontonextview | ||
|
||
import com.datadog.tools.annotation.NoOpImplementation | ||
|
||
@NoOpImplementation | ||
internal interface InteractionIngestionValidator { | ||
fun validate( | ||
context: InternalInteractionContext | ||
): Boolean | ||
} |
112 changes: 112 additions & 0 deletions
112
.../android/rum/internal/metric/interactiontonextview/InteractionToNextViewMetricResolver.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
/* | ||
* Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. | ||
* This product includes software developed at Datadog (https://www.datadoghq.com/). | ||
* Copyright 2016-Present Datadog, Inc. | ||
*/ | ||
|
||
package com.datadog.android.rum.internal.metric.interactiontonextview | ||
|
||
import androidx.annotation.VisibleForTesting | ||
import com.datadog.android.api.InternalLogger | ||
|
||
internal class InteractionToNextViewMetricResolver( | ||
private val internalLogger: InternalLogger, | ||
private val ingestionValidator: InteractionIngestionValidator = ActionTypeInteractionValidator(), | ||
private val lastInteractionIdentifier: LastInteractionIdentifier = TimeBasedInteractionIdentifier() | ||
) { | ||
|
||
private val lastInteractions = LinkedHashMap<String, InternalInteractionContext>() | ||
private val lastViewCreatedTimestamps = LinkedHashMap<String, Long>() | ||
|
||
fun onViewCreated(viewId: String, timestamp: Long) { | ||
lastViewCreatedTimestamps[viewId] = timestamp | ||
purgeOldEntries() | ||
} | ||
|
||
fun onActionSent(context: InternalInteractionContext) { | ||
if (ingestionValidator.validate(context)) { | ||
lastInteractions[context.viewId] = context | ||
} | ||
purgeOldEntries() | ||
} | ||
|
||
@Suppress("ReturnCount") | ||
fun resolveMetric(viewId: String): Long? { | ||
purgeOldEntries() | ||
val currentViewCreatedTimestamp = lastViewCreatedTimestamps[viewId] | ||
if (currentViewCreatedTimestamp == null) { | ||
internalLogger.log( | ||
InternalLogger.Level.WARN, | ||
InternalLogger.Target.MAINTAINER, | ||
{ "[ViewNetworkSettledMetric] The view was not yet created for this viewId:$viewId" } | ||
) | ||
return null | ||
} | ||
val lastPrevViewInteraction = resolveLastInteraction(viewId, currentViewCreatedTimestamp) | ||
if (lastPrevViewInteraction != null) { | ||
val difference = currentViewCreatedTimestamp - lastPrevViewInteraction.eventCreatedAtNanos | ||
if (difference > 0) { | ||
return difference | ||
} else { | ||
internalLogger.log( | ||
InternalLogger.Level.WARN, | ||
InternalLogger.Target.MAINTAINER, | ||
{ | ||
"[ViewNetworkSettledMetric] The difference between the last interaction " + | ||
"and the current view is negative for viewId:$viewId" | ||
} | ||
) | ||
return null | ||
} | ||
} | ||
internalLogger.log( | ||
InternalLogger.Level.WARN, | ||
InternalLogger.Target.MAINTAINER, | ||
{ "[ViewNetworkSettledMetric] No previous interaction found for this viewId:$viewId" } | ||
) | ||
return null | ||
} | ||
|
||
private fun resolveLastInteraction(viewId: String, currentViewCreatedTimestamp: Long): InternalInteractionContext? { | ||
val currentViewIdIndex = lastViewCreatedTimestamps.keys.indexOf(viewId) | ||
val previousViewId = lastViewCreatedTimestamps.keys.elementAtOrNull(currentViewIdIndex - 1) | ||
if (previousViewId != null) { | ||
lastInteractions[previousViewId]?.let { | ||
val context = PreviousViewLastInteractionContext( | ||
it.actionType, | ||
it.eventCreatedAtNanos, | ||
currentViewCreatedTimestamp | ||
) | ||
if (lastInteractionIdentifier.validate(context)) { | ||
return it | ||
} | ||
} | ||
} | ||
return null | ||
} | ||
|
||
private fun purgeOldEntries() { | ||
while (lastInteractions.entries.size > MAX_ENTRIES) { | ||
lastInteractions.entries.remove(lastInteractions.entries.first()) | ||
} | ||
while (lastViewCreatedTimestamps.size > MAX_ENTRIES) { | ||
lastViewCreatedTimestamps.remove(lastViewCreatedTimestamps.keys.first()) | ||
} | ||
} | ||
|
||
@VisibleForTesting | ||
internal fun lasInteractions(): Map<String, InternalInteractionContext> { | ||
return lastInteractions | ||
} | ||
|
||
@VisibleForTesting | ||
internal fun lastViewCreatedTimestamps(): Map<String, Long> { | ||
return lastViewCreatedTimestamps | ||
} | ||
|
||
companion object { | ||
// we need to keep at least 10 entries to be able to calculate the metric for consecutive views that | ||
// are going to be created almost in the same time and will rely on the previous view interaction | ||
private const val MAX_ENTRIES = 10 | ||
} | ||
} |
15 changes: 15 additions & 0 deletions
15
...m/datadog/android/rum/internal/metric/interactiontonextview/InternalInteractionContext.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
/* | ||
* Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. | ||
* This product includes software developed at Datadog (https://www.datadoghq.com/). | ||
* Copyright 2016-Present Datadog, Inc. | ||
*/ | ||
|
||
package com.datadog.android.rum.internal.metric.interactiontonextview | ||
|
||
import com.datadog.android.rum.RumActionType | ||
|
||
internal data class InternalInteractionContext( | ||
internal val viewId: String, | ||
internal val actionType: RumActionType, | ||
internal val eventCreatedAtNanos: Long | ||
) |
14 changes: 14 additions & 0 deletions
14
...om/datadog/android/rum/internal/metric/interactiontonextview/LastInteractionIdentifier.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
/* | ||
* Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. | ||
* This product includes software developed at Datadog (https://www.datadoghq.com/). | ||
* Copyright 2016-Present Datadog, Inc. | ||
*/ | ||
|
||
package com.datadog.android.rum.internal.metric.interactiontonextview | ||
|
||
import com.datadog.tools.annotation.NoOpImplementation | ||
|
||
@NoOpImplementation | ||
internal interface LastInteractionIdentifier { | ||
fun validate(context: PreviousViewLastInteractionContext): Boolean | ||
} |
15 changes: 15 additions & 0 deletions
15
...g/android/rum/internal/metric/interactiontonextview/PreviousViewLastInteractionContext.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
/* | ||
* Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. | ||
* This product includes software developed at Datadog (https://www.datadoghq.com/). | ||
* Copyright 2016-Present Datadog, Inc. | ||
*/ | ||
|
||
package com.datadog.android.rum.internal.metric.interactiontonextview | ||
|
||
import com.datadog.android.rum.RumActionType | ||
|
||
internal data class PreviousViewLastInteractionContext( | ||
internal val actionType: RumActionType, | ||
internal val eventCreatedAtNanos: Long, | ||
internal val currentViewCreationTimestamp: Long? | ||
) |
24 changes: 24 additions & 0 deletions
24
...tadog/android/rum/internal/metric/interactiontonextview/TimeBasedInteractionIdentifier.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
/* | ||
* Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. | ||
* This product includes software developed at Datadog (https://www.datadoghq.com/). | ||
* Copyright 2016-Present Datadog, Inc. | ||
*/ | ||
|
||
package com.datadog.android.rum.internal.metric.interactiontonextview | ||
|
||
import java.util.concurrent.TimeUnit | ||
|
||
internal class TimeBasedInteractionIdentifier( | ||
private val timeThresholdInNanoSeconds: Long = TimeUnit.MILLISECONDS.toNanos(DEFAULT_TIME_THRESHOLD_MS) | ||
) : LastInteractionIdentifier { | ||
|
||
override fun validate(context: PreviousViewLastInteractionContext): Boolean { | ||
return context.currentViewCreationTimestamp?.let { viewCreatedTime -> | ||
context.eventCreatedAtNanos - viewCreatedTime < timeThresholdInNanoSeconds | ||
} ?: false | ||
} | ||
|
||
companion object { | ||
internal const val DEFAULT_TIME_THRESHOLD_MS = 3000L | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.