Skip to content

Commit c0c3b79

Browse files
committed
PANA-8500: Add composition pipeline selection
1 parent a5846da commit c0c3b79

13 files changed

Lines changed: 371 additions & 27 deletions

File tree

features/dd-sdk-android-session-replay/api/apiSurface

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ enum com.datadog.android.sessionreplay.TouchPrivacy : PrivacyLevel
7777
class com.datadog.android.sessionreplay._SessionReplayInternalProxy
7878
constructor(SessionReplayConfiguration.Builder)
7979
fun setInternalCallback(SessionReplayInternalCallback): SessionReplayConfiguration.Builder
80+
fun setLayerTreeRecordingEnabled(Boolean): SessionReplayConfiguration.Builder
8081
class com.datadog.android.sessionreplay.internal.TouchPrivacyManager
8182
constructor(com.datadog.android.sessionreplay.TouchPrivacy)
8283
fun addTouchOverrideArea(android.graphics.Rect, com.datadog.android.sessionreplay.TouchPrivacy)

features/dd-sdk-android-session-replay/api/dd-sdk-android-session-replay.api

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,8 @@ public final class com/datadog/android/sessionreplay/SessionReplay {
6464
}
6565

6666
public final class com/datadog/android/sessionreplay/SessionReplayConfiguration {
67-
public final fun copy (Ljava/lang/String;Lcom/datadog/android/sessionreplay/SessionReplayPrivacy;Ljava/util/List;Ljava/util/List;Ljava/util/List;FLcom/datadog/android/sessionreplay/ImagePrivacy;ZLcom/datadog/android/sessionreplay/TouchPrivacy;Lcom/datadog/android/sessionreplay/TextAndInputPrivacy;ZLcom/datadog/android/sessionreplay/SystemRequirementsConfiguration;Lcom/datadog/android/sessionreplay/SessionReplayInternalCallback;Z)Lcom/datadog/android/sessionreplay/SessionReplayConfiguration;
68-
public static synthetic fun copy$default (Lcom/datadog/android/sessionreplay/SessionReplayConfiguration;Ljava/lang/String;Lcom/datadog/android/sessionreplay/SessionReplayPrivacy;Ljava/util/List;Ljava/util/List;Ljava/util/List;FLcom/datadog/android/sessionreplay/ImagePrivacy;ZLcom/datadog/android/sessionreplay/TouchPrivacy;Lcom/datadog/android/sessionreplay/TextAndInputPrivacy;ZLcom/datadog/android/sessionreplay/SystemRequirementsConfiguration;Lcom/datadog/android/sessionreplay/SessionReplayInternalCallback;ZILjava/lang/Object;)Lcom/datadog/android/sessionreplay/SessionReplayConfiguration;
67+
public final fun copy (Ljava/lang/String;Lcom/datadog/android/sessionreplay/SessionReplayPrivacy;Ljava/util/List;Ljava/util/List;Ljava/util/List;FLcom/datadog/android/sessionreplay/ImagePrivacy;ZLcom/datadog/android/sessionreplay/TouchPrivacy;Lcom/datadog/android/sessionreplay/TextAndInputPrivacy;ZLcom/datadog/android/sessionreplay/SystemRequirementsConfiguration;Lcom/datadog/android/sessionreplay/SessionReplayInternalCallback;ZZ)Lcom/datadog/android/sessionreplay/SessionReplayConfiguration;
68+
public static synthetic fun copy$default (Lcom/datadog/android/sessionreplay/SessionReplayConfiguration;Ljava/lang/String;Lcom/datadog/android/sessionreplay/SessionReplayPrivacy;Ljava/util/List;Ljava/util/List;Ljava/util/List;FLcom/datadog/android/sessionreplay/ImagePrivacy;ZLcom/datadog/android/sessionreplay/TouchPrivacy;Lcom/datadog/android/sessionreplay/TextAndInputPrivacy;ZLcom/datadog/android/sessionreplay/SystemRequirementsConfiguration;Lcom/datadog/android/sessionreplay/SessionReplayInternalCallback;ZZILjava/lang/Object;)Lcom/datadog/android/sessionreplay/SessionReplayConfiguration;
6969
public fun equals (Ljava/lang/Object;)Z
7070
public fun hashCode ()I
7171
public fun toString ()Ljava/lang/String;
@@ -148,6 +148,7 @@ public final class com/datadog/android/sessionreplay/TouchPrivacy : java/lang/En
148148
public final class com/datadog/android/sessionreplay/_SessionReplayInternalProxy {
149149
public fun <init> (Lcom/datadog/android/sessionreplay/SessionReplayConfiguration$Builder;)V
150150
public final fun setInternalCallback (Lcom/datadog/android/sessionreplay/SessionReplayInternalCallback;)Lcom/datadog/android/sessionreplay/SessionReplayConfiguration$Builder;
151+
public final fun setLayerTreeRecordingEnabled (Z)Lcom/datadog/android/sessionreplay/SessionReplayConfiguration$Builder;
151152
}
152153

153154
public final class com/datadog/android/sessionreplay/internal/TouchPrivacyManager {

features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/SessionReplay.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,8 @@ object SessionReplay {
6161
startRecordingImmediately = sessionReplayConfiguration.startRecordingImmediately,
6262
dynamicOptimizationEnabled = sessionReplayConfiguration.dynamicOptimizationEnabled,
6363
internalCallback = sessionReplayConfiguration.internalCallback,
64-
heatmapsEnabled = sessionReplayConfiguration.heatmapsEnabled
64+
heatmapsEnabled = sessionReplayConfiguration.heatmapsEnabled,
65+
layerTreeRecordingEnabled = sessionReplayConfiguration.layerTreeRecordingEnabled
6566
)
6667

6768
if (isAlreadyRegistered()) {

features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/SessionReplayConfiguration.kt

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ package com.datadog.android.sessionreplay
88

99
import androidx.annotation.FloatRange
1010
import com.datadog.android.api.InternalLogger
11+
import com.datadog.android.sessionreplay.internal.composition.SessionReplayLayerTreeRecordingFeatureFlag
1112
import com.datadog.android.sessionreplay.internal.recorder.SessionReplayRecorder
1213
import com.datadog.android.sessionreplay.recorder.OptionSelectorDetector
1314
import com.datadog.android.sessionreplay.utils.DrawableToColorMapper
@@ -30,7 +31,8 @@ data class SessionReplayConfiguration internal constructor(
3031
internal val dynamicOptimizationEnabled: Boolean,
3132
internal val systemRequirementsConfiguration: SystemRequirementsConfiguration,
3233
internal val internalCallback: SessionReplayInternalCallback,
33-
internal val heatmapsEnabled: Boolean
34+
internal val heatmapsEnabled: Boolean,
35+
internal val layerTreeRecordingEnabled: Boolean
3436
) {
3537

3638
/**
@@ -78,6 +80,7 @@ data class SessionReplayConfiguration internal constructor(
7880
private var systemRequirementsConfiguration = SystemRequirementsConfiguration.NONE
7981
private var internalCallback: SessionReplayInternalCallback = NoOpSessionReplayInternalCallback()
8082
private var heatmapsEnabled = false
83+
private var layerTreeRecordingEnabled = SessionReplayLayerTreeRecordingFeatureFlag.DEFAULT_ENABLED
8184

8285
/**
8386
* Adds an extension support implementation. This is mostly used when you want to provide
@@ -239,6 +242,17 @@ data class SessionReplayConfiguration internal constructor(
239242
return this
240243
}
241244

245+
/**
246+
* Enables the experimental layer-tree recording pipeline.
247+
*
248+
* This is exposed through [_SessionReplayInternalProxy] and is not part of the stable
249+
* customer-facing configuration API.
250+
*/
251+
internal fun setLayerTreeRecordingEnabled(enabled: Boolean): Builder {
252+
layerTreeRecordingEnabled = enabled
253+
return this
254+
}
255+
242256
/**
243257
* Builds a [SessionReplayConfiguration] based on the current state of this Builder.
244258
*/
@@ -257,7 +271,8 @@ data class SessionReplayConfiguration internal constructor(
257271
dynamicOptimizationEnabled = dynamicOptimizationEnabled,
258272
systemRequirementsConfiguration = systemRequirementsConfiguration,
259273
internalCallback = internalCallback,
260-
heatmapsEnabled = heatmapsEnabled
274+
heatmapsEnabled = heatmapsEnabled,
275+
layerTreeRecordingEnabled = layerTreeRecordingEnabled
261276
)
262277
}
263278

features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/_SessionReplayInternalProxy.kt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,16 @@ class _SessionReplayInternalProxy(private val builder: SessionReplayConfiguratio
3535
): SessionReplayConfiguration.Builder {
3636
return builder.setInternalCallback(internalCallback)
3737
}
38+
39+
/**
40+
* Enables or disables the experimental layer-tree recording pipeline.
41+
*
42+
* This mirrors the iOS `layerTreeRecording` Session Replay feature flag.
43+
*
44+
* @param enabled whether layer-tree recording should be used.
45+
* @return [SessionReplayConfiguration.Builder] instance.
46+
*/
47+
fun setLayerTreeRecordingEnabled(enabled: Boolean): SessionReplayConfiguration.Builder {
48+
return builder.setLayerTreeRecordingEnabled(enabled)
49+
}
3850
}

features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/DefaultRecorderProvider.kt

Lines changed: 34 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ import com.datadog.android.sessionreplay.ImagePrivacy
2727
import com.datadog.android.sessionreplay.MapperTypeWrapper
2828
import com.datadog.android.sessionreplay.SessionReplayInternalCallback
2929
import com.datadog.android.sessionreplay.TextAndInputPrivacy
30+
import com.datadog.android.sessionreplay.internal.composition.CapturePipelineSelector
31+
import com.datadog.android.sessionreplay.internal.composition.CompositionCapturePipeline
32+
import com.datadog.android.sessionreplay.internal.composition.SessionReplayLayerTreeRecordingFeatureFlag
3033
import com.datadog.android.sessionreplay.internal.recorder.Recorder
3134
import com.datadog.android.sessionreplay.internal.recorder.SessionReplayRecorder
3235
import com.datadog.android.sessionreplay.internal.recorder.mapper.ActionBarContainerMapper
@@ -67,7 +70,9 @@ internal class DefaultRecorderProvider(
6770
private val customDrawableMappers: List<DrawableToColorMapper>,
6871
private val dynamicOptimizationEnabled: Boolean,
6972
private val internalCallback: SessionReplayInternalCallback,
70-
private val heatmapsEnabled: Boolean
73+
private val heatmapsEnabled: Boolean,
74+
private val layerTreeRecordingEnabled: Boolean = SessionReplayLayerTreeRecordingFeatureFlag.DEFAULT_ENABLED,
75+
private val compositionPipelineFactory: () -> Recorder = { CompositionCapturePipeline() }
7176
) : RecorderProvider {
7277

7378
override fun provideSessionReplayRecorder(
@@ -77,24 +82,34 @@ internal class DefaultRecorderProvider(
7782
rumContextProvider: RumContextProvider,
7883
application: Application
7984
): Recorder {
80-
return SessionReplayRecorder(
81-
application,
82-
resourceDataStoreManager = resourceDataStoreManager,
83-
resourcesWriter = resourceWriter,
84-
rumContextProvider = rumContextProvider,
85-
imagePrivacy = imagePrivacy,
86-
touchPrivacyManager = touchPrivacyManager,
87-
textAndInputPrivacy = textAndInputPrivacy,
88-
recordWriter = recordWriter,
89-
timeProvider = sdkCore.timeProvider,
90-
mappers = customMappers + builtInMappers(),
91-
customOptionSelectorDetectors = customOptionSelectorDetectors,
92-
customDrawableMappers = customDrawableMappers,
93-
sdkCore = sdkCore,
94-
dynamicOptimizationEnabled = dynamicOptimizationEnabled,
95-
internalCallback = internalCallback,
96-
heatmapIdentifierRegistry = if (heatmapsEnabled) LazyHeatmapIdentifierRegistry(sdkCore) else null
97-
)
85+
return CapturePipelineSelector(
86+
compositionEnabled = layerTreeRecordingEnabled,
87+
compositionFactory = compositionPipelineFactory,
88+
legacyFactory = {
89+
SessionReplayRecorder(
90+
application,
91+
resourceDataStoreManager = resourceDataStoreManager,
92+
resourcesWriter = resourceWriter,
93+
rumContextProvider = rumContextProvider,
94+
imagePrivacy = imagePrivacy,
95+
touchPrivacyManager = touchPrivacyManager,
96+
textAndInputPrivacy = textAndInputPrivacy,
97+
recordWriter = recordWriter,
98+
timeProvider = sdkCore.timeProvider,
99+
mappers = customMappers + builtInMappers(),
100+
customOptionSelectorDetectors = customOptionSelectorDetectors,
101+
customDrawableMappers = customDrawableMappers,
102+
sdkCore = sdkCore,
103+
dynamicOptimizationEnabled = dynamicOptimizationEnabled,
104+
internalCallback = internalCallback,
105+
heatmapIdentifierRegistry = if (heatmapsEnabled) {
106+
LazyHeatmapIdentifierRegistry(sdkCore)
107+
} else {
108+
null
109+
}
110+
)
111+
}
112+
).create()
98113
}
99114

100115
@Suppress("LongMethod")

features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/SessionReplayFeature.kt

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import com.datadog.android.sessionreplay.SessionReplayInternalCallback
2525
import com.datadog.android.sessionreplay.SessionReplayPrivacy
2626
import com.datadog.android.sessionreplay.TextAndInputPrivacy
2727
import com.datadog.android.sessionreplay.TouchPrivacy
28+
import com.datadog.android.sessionreplay.internal.composition.SessionReplayLayerTreeRecordingFeatureFlag
2829
import com.datadog.android.sessionreplay.internal.net.BatchesToSegmentsMapper
2930
import com.datadog.android.sessionreplay.internal.net.SegmentRequestFactory
3031
import com.datadog.android.sessionreplay.internal.recorder.NoOpRecorder
@@ -75,7 +76,8 @@ internal class SessionReplayFeature(
7576
startRecordingImmediately: Boolean,
7677
dynamicOptimizationEnabled: Boolean,
7778
internalCallback: SessionReplayInternalCallback,
78-
heatmapsEnabled: Boolean
79+
heatmapsEnabled: Boolean,
80+
layerTreeRecordingEnabled: Boolean = SessionReplayLayerTreeRecordingFeatureFlag.DEFAULT_ENABLED
7981
) : this(
8082
sdkCore,
8183
customEndpointUrl,
@@ -95,7 +97,8 @@ internal class SessionReplayFeature(
9597
customDrawableMappers,
9698
dynamicOptimizationEnabled,
9799
internalCallback,
98-
heatmapsEnabled
100+
heatmapsEnabled,
101+
layerTreeRecordingEnabled
99102
)
100103
)
101104

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
* Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0.
3+
* This product includes software developed at Datadog (https://www.datadoghq.com/).
4+
* Copyright 2016-Present Datadog, Inc.
5+
*/
6+
7+
package com.datadog.android.sessionreplay.internal.composition
8+
9+
import com.datadog.android.sessionreplay.internal.recorder.Recorder
10+
11+
internal object SessionReplayLayerTreeRecordingFeatureFlag {
12+
const val DEFAULT_ENABLED: Boolean = false
13+
}
14+
15+
internal class CapturePipelineSelector(
16+
private val compositionEnabled: Boolean = SessionReplayLayerTreeRecordingFeatureFlag.DEFAULT_ENABLED,
17+
private val legacyFactory: () -> Recorder,
18+
private val compositionFactory: () -> Recorder
19+
) {
20+
fun create(): Recorder = if (compositionEnabled) compositionFactory() else legacyFactory()
21+
}
22+
23+
/**
24+
* Lifecycle entry point for the composition capture pipeline. Traversal and capture are added by
25+
* later workstreams; keeping this recorder isolated prevents legacy state from crossing pipelines.
26+
*/
27+
internal class CompositionCapturePipeline : Recorder {
28+
override fun registerCallbacks() = Unit
29+
30+
override fun unregisterCallbacks() = Unit
31+
32+
override fun stopProcessingRecords() = Unit
33+
34+
override fun resumeRecorders() = Unit
35+
36+
override fun stopRecorders() = Unit
37+
}

features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/SessionReplayConfigurationBuilderTest.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ internal class SessionReplayConfigurationBuilderTest {
7676
assertThat(sessionReplayConfiguration.customOptionSelectorDetectors).isEmpty()
7777
assertThat(sessionReplayConfiguration.dynamicOptimizationEnabled).isEqualTo(true)
7878
assertThat(sessionReplayConfiguration.heatmapsEnabled).isFalse()
79+
assertThat(sessionReplayConfiguration.layerTreeRecordingEnabled).isFalse()
7980
}
8081

8182
@Test

features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/SessionReplayInternalProxyTest.kt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,4 +48,19 @@ internal class SessionReplayInternalProxyTest {
4848
assertThat(result).isEqualTo(testedBuilder)
4949
assertThat(sessionReplayConfiguration.internalCallback).isEqualTo(mockInternalCallback)
5050
}
51+
52+
@Test
53+
fun `M enable layer tree recording W setLayerTreeRecordingEnabled()`() {
54+
// Given
55+
testedBuilder = SessionReplayConfiguration.Builder(fakeSampleRate)
56+
testedProxy = _SessionReplayInternalProxy(testedBuilder)
57+
58+
// When
59+
val result = testedProxy.setLayerTreeRecordingEnabled(true)
60+
val sessionReplayConfiguration = result.build()
61+
62+
// Then
63+
assertThat(result).isSameAs(testedBuilder)
64+
assertThat(sessionReplayConfiguration.layerTreeRecordingEnabled).isTrue()
65+
}
5166
}

0 commit comments

Comments
 (0)