Skip to content

Commit 5a98bd8

Browse files
authored
Merge pull request #2516 from DataDog/carlosnogueira/RUM-8652/add-config-option-to-override-specific-sr-methods
[RUM-8652] Allow definition of custom implementations of specific SR methods
2 parents a9007ed + f0da63c commit 5a98bd8

14 files changed

+203
-24
lines changed

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ data class com.datadog.android.sessionreplay.SessionReplayConfiguration
3636
fun setDynamicOptimizationEnabled(Boolean): Builder
3737
fun setSystemRequirements(SystemRequirementsConfiguration): Builder
3838
fun build(): SessionReplayConfiguration
39+
interface com.datadog.android.sessionreplay.SessionReplayInternalCallback
40+
fun getCurrentActivity(): android.app.Activity?
3941
enum com.datadog.android.sessionreplay.SessionReplayPrivacy
4042
- ALLOW
4143
- MASK
@@ -55,6 +57,9 @@ enum com.datadog.android.sessionreplay.TextAndInputPrivacy : PrivacyLevel
5557
enum com.datadog.android.sessionreplay.TouchPrivacy : PrivacyLevel
5658
- SHOW
5759
- HIDE
60+
class com.datadog.android.sessionreplay._SessionReplayInternalProxy
61+
constructor(SessionReplayConfiguration.Builder)
62+
fun setInternalCallback(SessionReplayInternalCallback): SessionReplayConfiguration.Builder
5863
class com.datadog.android.sessionreplay.internal.TouchPrivacyManager
5964
constructor(com.datadog.android.sessionreplay.TouchPrivacy)
6065
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: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,8 @@ public final class com/datadog/android/sessionreplay/SessionReplay {
4646
}
4747

4848
public final class com/datadog/android/sessionreplay/SessionReplayConfiguration {
49-
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/SessionReplayConfiguration;
50-
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;ILjava/lang/Object;)Lcom/datadog/android/sessionreplay/SessionReplayConfiguration;
49+
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;)Lcom/datadog/android/sessionreplay/SessionReplayConfiguration;
50+
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;ILjava/lang/Object;)Lcom/datadog/android/sessionreplay/SessionReplayConfiguration;
5151
public fun equals (Ljava/lang/Object;)Z
5252
public fun hashCode ()I
5353
public fun toString ()Ljava/lang/String;
@@ -69,6 +69,10 @@ public final class com/datadog/android/sessionreplay/SessionReplayConfiguration$
6969
public final fun useCustomEndpoint (Ljava/lang/String;)Lcom/datadog/android/sessionreplay/SessionReplayConfiguration$Builder;
7070
}
7171

72+
public abstract interface class com/datadog/android/sessionreplay/SessionReplayInternalCallback {
73+
public abstract fun getCurrentActivity ()Landroid/app/Activity;
74+
}
75+
7276
public final class com/datadog/android/sessionreplay/SessionReplayPrivacy : java/lang/Enum {
7377
public static final field ALLOW Lcom/datadog/android/sessionreplay/SessionReplayPrivacy;
7478
public static final field MASK Lcom/datadog/android/sessionreplay/SessionReplayPrivacy;
@@ -108,6 +112,11 @@ public final class com/datadog/android/sessionreplay/TouchPrivacy : java/lang/En
108112
public static fun values ()[Lcom/datadog/android/sessionreplay/TouchPrivacy;
109113
}
110114

115+
public final class com/datadog/android/sessionreplay/_SessionReplayInternalProxy {
116+
public fun <init> (Lcom/datadog/android/sessionreplay/SessionReplayConfiguration$Builder;)V
117+
public final fun setInternalCallback (Lcom/datadog/android/sessionreplay/SessionReplayInternalCallback;)Lcom/datadog/android/sessionreplay/SessionReplayConfiguration$Builder;
118+
}
119+
111120
public final class com/datadog/android/sessionreplay/internal/TouchPrivacyManager {
112121
public fun <init> (Lcom/datadog/android/sessionreplay/TouchPrivacy;)V
113122
public final fun addTouchOverrideArea (Landroid/graphics/Rect;Lcom/datadog/android/sessionreplay/TouchPrivacy;)V

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
@@ -59,7 +59,8 @@ object SessionReplay {
5959
customDrawableMappers = sessionReplayConfiguration.customDrawableMappers,
6060
sampleRate = sessionReplayConfiguration.sampleRate,
6161
startRecordingImmediately = sessionReplayConfiguration.startRecordingImmediately,
62-
dynamicOptimizationEnabled = sessionReplayConfiguration.dynamicOptimizationEnabled
62+
dynamicOptimizationEnabled = sessionReplayConfiguration.dynamicOptimizationEnabled,
63+
internalCallback = sessionReplayConfiguration.internalCallback
6364
)
6465

6566
if (isAlreadyRegistered()) {

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

Lines changed: 19 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.recorder.SessionReplayRecorder
1112
import com.datadog.android.sessionreplay.recorder.OptionSelectorDetector
1213
import com.datadog.android.sessionreplay.utils.DrawableToColorMapper
1314
import java.util.Locale
@@ -27,7 +28,8 @@ data class SessionReplayConfiguration internal constructor(
2728
internal val touchPrivacy: TouchPrivacy,
2829
internal val textAndInputPrivacy: TextAndInputPrivacy,
2930
internal val dynamicOptimizationEnabled: Boolean,
30-
internal val systemRequirementsConfiguration: SystemRequirementsConfiguration
31+
internal val systemRequirementsConfiguration: SystemRequirementsConfiguration,
32+
internal val internalCallback: SessionReplayInternalCallback
3133
) {
3234

3335
/**
@@ -73,6 +75,7 @@ data class SessionReplayConfiguration internal constructor(
7375
private var extensionSupportSet: MutableSet<ExtensionSupport> = mutableSetOf()
7476
private var dynamicOptimizationEnabled = true
7577
private var systemRequirementsConfiguration = SystemRequirementsConfiguration.NONE
78+
private var internalCallback: SessionReplayInternalCallback = NoOpSessionReplayInternalCallback()
7679

7780
/**
7881
* Adds an extension support implementation. This is mostly used when you want to provide
@@ -210,6 +213,19 @@ data class SessionReplayConfiguration internal constructor(
210213
return this
211214
}
212215

216+
/**
217+
* Allows definition of custom callback functions for Session Replay
218+
* that may require platform-specific behavior.
219+
* Currently, this enables defining:
220+
* - [SessionReplayInternalCallback.getCurrentActivity]:
221+
* Used in [SessionReplayRecorder] to register fragment lifecycle callbacks
222+
* for clients initialized after the `Application.onCreate` phase.
223+
*/
224+
internal fun setInternalCallback(internalCallback: SessionReplayInternalCallback): Builder {
225+
this.internalCallback = internalCallback
226+
return this
227+
}
228+
213229
/**
214230
* Builds a [SessionReplayConfiguration] based on the current state of this Builder.
215231
*/
@@ -226,7 +242,8 @@ data class SessionReplayConfiguration internal constructor(
226242
sampleRate = sampleRate,
227243
startRecordingImmediately = startRecordingImmediately,
228244
dynamicOptimizationEnabled = dynamicOptimizationEnabled,
229-
systemRequirementsConfiguration = systemRequirementsConfiguration
245+
systemRequirementsConfiguration = systemRequirementsConfiguration,
246+
internalCallback = internalCallback
230247
)
231248
}
232249

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
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
8+
9+
import android.app.Activity
10+
import com.datadog.android.sessionreplay.internal.recorder.SessionReplayRecorder
11+
import com.datadog.tools.annotation.NoOpImplementation
12+
13+
/**
14+
* Defines points of change for clients to override existing functionality with platform specific code.
15+
*/
16+
@NoOpImplementation
17+
interface SessionReplayInternalCallback {
18+
/**
19+
* Retrieves the current activity, allowing clients to pass it when needed.
20+
* This is used by [SessionReplayRecorder] to register fragment lifecycle callbacks
21+
* that were missed because the client was initialized after the `Application.onCreate` phase.
22+
*/
23+
fun getCurrentActivity(): Activity?
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
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
8+
9+
import com.datadog.android.lint.InternalApi
10+
11+
/**
12+
* This class exposes internal methods that are used by other Datadog modules and cross platform
13+
* frameworks. It is not meant for public use.
14+
*
15+
* DO NOT USE this class or its methods if you are not working on the internals of the Datadog SDK
16+
* or one of the cross platform frameworks.
17+
*
18+
* Methods, members, and functionality of this class are subject to change without notice, as they
19+
* are not considered part of the public interface of the Datadog SDK.
20+
*/
21+
@InternalApi
22+
@Suppress(
23+
"ClassName",
24+
"ClassNaming"
25+
)
26+
class _SessionReplayInternalProxy(private val builder: SessionReplayConfiguration.Builder) {
27+
/**
28+
* Sets an internal callback for session replay.
29+
*
30+
* @param internalCallback callback instance to override specific parts of the codebase.
31+
* @return [SessionReplayConfiguration.Builder] instance.
32+
*/
33+
fun setInternalCallback(
34+
internalCallback: SessionReplayInternalCallback
35+
): SessionReplayConfiguration.Builder {
36+
return builder.setInternalCallback(internalCallback)
37+
}
38+
}

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import com.datadog.android.api.feature.FeatureSdkCore
2525
import com.datadog.android.internal.utils.ImageViewUtils
2626
import com.datadog.android.sessionreplay.ImagePrivacy
2727
import com.datadog.android.sessionreplay.MapperTypeWrapper
28+
import com.datadog.android.sessionreplay.SessionReplayInternalCallback
2829
import com.datadog.android.sessionreplay.TextAndInputPrivacy
2930
import com.datadog.android.sessionreplay.internal.recorder.Recorder
3031
import com.datadog.android.sessionreplay.internal.recorder.SessionReplayRecorder
@@ -64,7 +65,8 @@ internal class DefaultRecorderProvider(
6465
private val customMappers: List<MapperTypeWrapper<*>>,
6566
private val customOptionSelectorDetectors: List<OptionSelectorDetector>,
6667
private val customDrawableMappers: List<DrawableToColorMapper>,
67-
private val dynamicOptimizationEnabled: Boolean
68+
private val dynamicOptimizationEnabled: Boolean,
69+
private val internalCallback: SessionReplayInternalCallback
6870
) : RecorderProvider {
6971

7072
override fun provideSessionReplayRecorder(
@@ -87,7 +89,8 @@ internal class DefaultRecorderProvider(
8789
customOptionSelectorDetectors = customOptionSelectorDetectors,
8890
customDrawableMappers = customDrawableMappers,
8991
sdkCore = sdkCore,
90-
dynamicOptimizationEnabled = dynamicOptimizationEnabled
92+
dynamicOptimizationEnabled = dynamicOptimizationEnabled,
93+
internalCallback = internalCallback
9194
)
9295
}
9396

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
@@ -20,6 +20,7 @@ import com.datadog.android.core.sampling.RateBasedSampler
2020
import com.datadog.android.core.sampling.Sampler
2121
import com.datadog.android.sessionreplay.ImagePrivacy
2222
import com.datadog.android.sessionreplay.MapperTypeWrapper
23+
import com.datadog.android.sessionreplay.SessionReplayInternalCallback
2324
import com.datadog.android.sessionreplay.SessionReplayPrivacy
2425
import com.datadog.android.sessionreplay.TextAndInputPrivacy
2526
import com.datadog.android.sessionreplay.TouchPrivacy
@@ -71,7 +72,8 @@ internal class SessionReplayFeature(
7172
customDrawableMappers: List<DrawableToColorMapper>,
7273
sampleRate: Float,
7374
startRecordingImmediately: Boolean,
74-
dynamicOptimizationEnabled: Boolean
75+
dynamicOptimizationEnabled: Boolean,
76+
internalCallback: SessionReplayInternalCallback
7577
) : this(
7678
sdkCore,
7779
customEndpointUrl,
@@ -89,7 +91,8 @@ internal class SessionReplayFeature(
8991
customMappers,
9092
customOptionSelectorDetectors,
9193
customDrawableMappers,
92-
dynamicOptimizationEnabled
94+
dynamicOptimizationEnabled,
95+
internalCallback
9396
)
9497
)
9598

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

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,24 @@ internal class SessionReplayLifecycleCallback(
2121

2222
private val currentActiveWindows = WeakHashMap<Window, Any?>()
2323

24+
fun setCurrentWindow(activity: Activity) {
25+
activity.window?.let {
26+
currentActiveWindows[it] = null
27+
}
28+
}
29+
30+
fun registerFragmentLifecycleCallbacks(activity: Activity) {
31+
if (activity is FragmentActivity) {
32+
// we need to register before the activity resumes to catch all the fragments
33+
// added even before the activity resumes
34+
val lifecycleCallback = RecorderFragmentLifecycleCallback(this)
35+
activity.supportFragmentManager.registerFragmentLifecycleCallbacks(
36+
lifecycleCallback,
37+
true
38+
)
39+
}
40+
}
41+
2442
override fun onWindowsAdded(windows: List<Window>) {
2543
windows.forEach {
2644
currentActiveWindows[it] = null
@@ -41,15 +59,7 @@ internal class SessionReplayLifecycleCallback(
4159

4260
@MainThread
4361
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
44-
if (activity is FragmentActivity) {
45-
// we need to register before the activity resumes to catch all the fragments
46-
// added even before the activity resumes
47-
val lifecycleCallback = RecorderFragmentLifecycleCallback(this)
48-
activity.supportFragmentManager.registerFragmentLifecycleCallbacks(
49-
lifecycleCallback,
50-
true
51-
)
52-
}
62+
registerFragmentLifecycleCallbacks(activity)
5363
}
5464

5565
@MainThread

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

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import com.datadog.android.api.InternalLogger
1616
import com.datadog.android.api.feature.FeatureSdkCore
1717
import com.datadog.android.sessionreplay.ImagePrivacy
1818
import com.datadog.android.sessionreplay.MapperTypeWrapper
19+
import com.datadog.android.sessionreplay.SessionReplayInternalCallback
1920
import com.datadog.android.sessionreplay.TextAndInputPrivacy
2021
import com.datadog.android.sessionreplay.internal.LifecycleCallback
2122
import com.datadog.android.sessionreplay.internal.SessionReplayLifecycleCallback
@@ -71,7 +72,7 @@ internal class SessionReplayRecorder : OnWindowRefreshedCallback, Recorder {
7172
private val viewOnDrawInterceptor: ViewOnDrawInterceptor
7273
private val internalLogger: InternalLogger
7374
private val resourceDataStoreManager: ResourceDataStoreManager
74-
75+
private val internalCallback: SessionReplayInternalCallback
7576
private val uiHandler: Handler
7677
private var shouldRecord = false
7778

@@ -91,7 +92,8 @@ internal class SessionReplayRecorder : OnWindowRefreshedCallback, Recorder {
9192
windowInspector: WindowInspector = WindowInspector,
9293
sdkCore: FeatureSdkCore,
9394
resourceDataStoreManager: ResourceDataStoreManager,
94-
dynamicOptimizationEnabled: Boolean
95+
dynamicOptimizationEnabled: Boolean,
96+
internalCallback: SessionReplayInternalCallback
9597
) {
9698
val internalLogger = sdkCore.internalLogger
9799
val rumContextDataHandler = RumContextDataHandler(
@@ -129,6 +131,7 @@ internal class SessionReplayRecorder : OnWindowRefreshedCallback, Recorder {
129131
recordedDataQueue = ConcurrentLinkedQueue()
130132
)
131133
this.resourceDataStoreManager = resourceDataStoreManager
134+
this.internalCallback = internalCallback
132135

133136
val viewIdentifierResolver: ViewIdentifierResolver = DefaultViewIdentifierResolver
134137
val colorStringFormatter: ColorStringFormatter = DefaultColorStringFormatter
@@ -208,6 +211,13 @@ internal class SessionReplayRecorder : OnWindowRefreshedCallback, Recorder {
208211
touchPrivacyManager
209212
)
210213
this.sessionReplayLifecycleCallback = SessionReplayLifecycleCallback(this)
214+
215+
// Register fragment lifecycle callbacks for clients initialized after the Application.onCreate phase
216+
internalCallback.getCurrentActivity()?.let {
217+
sessionReplayLifecycleCallback.setCurrentWindow(it)
218+
sessionReplayLifecycleCallback.registerFragmentLifecycleCallbacks(it)
219+
}
220+
211221
this.uiHandler = Handler(Looper.getMainLooper())
212222
this.internalLogger = internalLogger
213223
}
@@ -231,7 +241,8 @@ internal class SessionReplayRecorder : OnWindowRefreshedCallback, Recorder {
231241
recordedDataQueueHandler: RecordedDataQueueHandler,
232242
resourceDataStoreManager: ResourceDataStoreManager,
233243
uiHandler: Handler,
234-
internalLogger: InternalLogger
244+
internalLogger: InternalLogger,
245+
internalCallback: SessionReplayInternalCallback
235246
) {
236247
this.appContext = appContext
237248
this.rumContextProvider = rumContextProvider
@@ -250,6 +261,7 @@ internal class SessionReplayRecorder : OnWindowRefreshedCallback, Recorder {
250261
this.uiHandler = uiHandler
251262
this.internalLogger = internalLogger
252263
this.resourceDataStoreManager = resourceDataStoreManager
264+
this.internalCallback = internalCallback
253265
}
254266

255267
override fun stopProcessingRecords() {

0 commit comments

Comments
 (0)