@datadog/mobile-react-native-session-replay 2.4.4-alpha.0 → 2.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/DatadogSDKReactNativeSessionReplay.podspec +1 -1
  2. package/android/build.gradle +11 -1
  3. package/android/consumer-proguard-rules.pro +3 -0
  4. package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/DdSessionReplayImplementation.kt +32 -27
  5. package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/ReactNativeSessionReplayExtensionSupport.kt +15 -4
  6. package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/ReactTextPropertiesResolver.kt +8 -10
  7. package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/SessionReplayPrivacySettings.kt +90 -0
  8. package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/SessionReplaySDKWrapper.kt +14 -0
  9. package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/SessionReplayWrapper.kt +10 -0
  10. package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/extensions/ReactDrawablesExt.kt +190 -0
  11. package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/mappers/ReactEditTextMapper.kt +84 -10
  12. package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/mappers/ReactNativeImageViewMapper.kt +106 -0
  13. package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/mappers/ReactViewGroupMapper.kt +4 -5
  14. package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/resources/ReactDrawableCopier.kt +34 -0
  15. package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/utils/DrawableUtils.kt +10 -23
  16. package/android/src/newarch/kotlin/com/datadog/reactnative/sessionreplay/DdSessionReplay.kt +28 -4
  17. package/android/src/oldarch/kotlin/com/datadog/reactnative/sessionreplay/DdSessionReplay.kt +35 -3
  18. package/android/src/{main/kotlin/com/datadog/reactnative/sessionreplay/extensions/LongExt.kt → rn75/kotlin/com/datadog/reactnative/sessionreplay/extensions/LengthPercentageExt.kt} +6 -8
  19. package/android/src/rn75/kotlin/com/datadog/reactnative/sessionreplay/utils/ReactViewBackgroundDrawableUtils.kt +101 -0
  20. package/android/src/rn76/kotlin/com/datadog/reactnative/sessionreplay/extensions/LengthPercentageExt.kt +13 -0
  21. package/android/src/rn76/kotlin/com/datadog/reactnative/sessionreplay/utils/ReactViewBackgroundDrawableUtils.kt +101 -0
  22. package/android/src/rnlegacy/kotlin/com.datadog.reactnative.sessionreplay/utils/ReactViewBackgroundDrawableUtils.kt +88 -0
  23. package/android/src/test/kotlin/com/datadog/reactnative/sessionreplay/DdSessionReplayImplementationTest.kt +59 -50
  24. package/android/src/test/kotlin/com/datadog/reactnative/sessionreplay/ReactNativeSessionReplayExtensionSupportTest.kt +7 -3
  25. package/android/src/test/kotlin/com/datadog/reactnative/sessionreplay/ReactTextPropertiesResolverTest.kt +2 -7
  26. package/android/src/test/kotlin/com/datadog/reactnative/sessionreplay/mappers/ReactViewGroupMapperTest.kt +2 -7
  27. package/android/src/test/kotlin/com/datadog/reactnative/sessionreplay/utils/DrawableUtilsTest.kt +2 -1
  28. package/ios/Sources/DdSessionReplay.mm +46 -4
  29. package/ios/Sources/DdSessionReplayImplementation.swift +83 -11
  30. package/ios/Sources/RCTTextViewRecorder.swift +1 -1
  31. package/lib/commonjs/SessionReplay.js +78 -10
  32. package/lib/commonjs/SessionReplay.js.map +1 -1
  33. package/lib/commonjs/index.js +18 -0
  34. package/lib/commonjs/index.js.map +1 -1
  35. package/lib/commonjs/specs/NativeDdSessionReplay.js.map +1 -1
  36. package/lib/module/SessionReplay.js +77 -9
  37. package/lib/module/SessionReplay.js.map +1 -1
  38. package/lib/module/index.js +2 -2
  39. package/lib/module/index.js.map +1 -1
  40. package/lib/module/specs/NativeDdSessionReplay.js.map +1 -1
  41. package/lib/typescript/SessionReplay.d.ts +73 -4
  42. package/lib/typescript/SessionReplay.d.ts.map +1 -1
  43. package/lib/typescript/index.d.ts +2 -2
  44. package/lib/typescript/index.d.ts.map +1 -1
  45. package/lib/typescript/nativeModulesTypes.d.ts +18 -3
  46. package/lib/typescript/nativeModulesTypes.d.ts.map +1 -1
  47. package/lib/typescript/specs/NativeDdSessionReplay.d.ts +15 -2
  48. package/lib/typescript/specs/NativeDdSessionReplay.d.ts.map +1 -1
  49. package/package.json +85 -84
  50. package/src/SessionReplay.ts +170 -23
  51. package/src/__tests__/SessionReplay.test.ts +94 -8
  52. package/src/index.ts +14 -2
  53. package/src/nativeModulesTypes.ts +27 -4
  54. package/src/specs/NativeDdSessionReplay.ts +21 -3
  55. package/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/utils/ReactViewBackgroundDrawableUtils.kt +0 -66
@@ -0,0 +1,101 @@
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
+ import android.graphics.drawable.Drawable
7
+ import android.graphics.drawable.InsetDrawable
8
+ import android.graphics.drawable.LayerDrawable
9
+ import com.datadog.android.internal.utils.densityNormalized
10
+ import com.datadog.android.sessionreplay.model.MobileSegment
11
+ import com.datadog.reactnative.sessionreplay.extensions.getRadius
12
+ import com.datadog.reactnative.sessionreplay.utils.DrawableUtils
13
+ import com.datadog.reactnative.sessionreplay.utils.formatAsRgba
14
+ import com.facebook.react.common.annotations.UnstableReactNativeAPI
15
+ import com.facebook.react.uimanager.Spacing
16
+ import com.facebook.react.uimanager.drawable.CSSBackgroundDrawable
17
+
18
+ internal class ReactViewBackgroundDrawableUtils : DrawableUtils() {
19
+ @OptIn(UnstableReactNativeAPI::class)
20
+ override fun resolveShapeAndBorder(
21
+ drawable: Drawable,
22
+ opacity: Float,
23
+ pixelDensity: Float
24
+ ): Pair<MobileSegment.ShapeStyle?, MobileSegment.ShapeBorder?> {
25
+ if (drawable !is CSSBackgroundDrawable) {
26
+ return null to null
27
+ }
28
+
29
+ val borderProps = resolveBorder(drawable, pixelDensity)
30
+ val backgroundColor = getBackgroundColor(drawable)
31
+ val colorHexString = if (backgroundColor != null) {
32
+ formatAsRgba(backgroundColor)
33
+ } else {
34
+ return null to borderProps
35
+ }
36
+
37
+ return MobileSegment.ShapeStyle(
38
+ colorHexString,
39
+ opacity,
40
+ getBorderRadius(drawable)
41
+ ) to borderProps
42
+ }
43
+
44
+ @OptIn(UnstableReactNativeAPI::class)
45
+ override fun getReactBackgroundFromDrawable(drawable: Drawable?): Drawable? {
46
+ if (drawable is CSSBackgroundDrawable) {
47
+ return drawable
48
+ }
49
+
50
+ if (drawable is InsetDrawable) {
51
+ return getReactBackgroundFromDrawable(drawable.drawable)
52
+ }
53
+
54
+ if (drawable is LayerDrawable) {
55
+ for (layerNumber in 0 until drawable.numberOfLayers) {
56
+ val layer = drawable.getDrawable(layerNumber)
57
+ if (layer is CSSBackgroundDrawable) {
58
+ return layer
59
+ }
60
+ }
61
+ }
62
+
63
+ return null
64
+ }
65
+
66
+ @OptIn(UnstableReactNativeAPI::class)
67
+ private fun getBorderRadius(drawable: CSSBackgroundDrawable): Float {
68
+ val width = drawable.intrinsicWidth.toFloat()
69
+ val height = drawable.intrinsicHeight.toFloat()
70
+ return drawable.borderRadius.uniform?.getRadius(width, height) ?: 0f
71
+ }
72
+
73
+ @OptIn(UnstableReactNativeAPI::class)
74
+ private fun getBackgroundColor(
75
+ backgroundDrawable: CSSBackgroundDrawable
76
+ ): Int? {
77
+ return reflectionUtils.getDeclaredField(
78
+ backgroundDrawable,
79
+ COLOR_FIELD_NAME
80
+ ) as Int?
81
+ }
82
+
83
+ @OptIn(UnstableReactNativeAPI::class)
84
+ private fun resolveBorder(
85
+ backgroundDrawable: CSSBackgroundDrawable,
86
+ pixelDensity: Float
87
+ ): MobileSegment.ShapeBorder {
88
+ val borderWidth =
89
+ backgroundDrawable.fullBorderWidth.toLong().densityNormalized(pixelDensity)
90
+ val borderColor = formatAsRgba(backgroundDrawable.getBorderColor(Spacing.ALL))
91
+
92
+ return MobileSegment.ShapeBorder(
93
+ color = borderColor,
94
+ width = borderWidth
95
+ )
96
+ }
97
+
98
+ private companion object {
99
+ private const val COLOR_FIELD_NAME = "mColor"
100
+ }
101
+ }
@@ -0,0 +1,88 @@
1
+ import android.graphics.drawable.Drawable
2
+ import android.graphics.drawable.InsetDrawable
3
+ import android.graphics.drawable.LayerDrawable
4
+ import com.datadog.android.internal.utils.densityNormalized
5
+ import com.datadog.android.sessionreplay.model.MobileSegment
6
+ import com.datadog.reactnative.sessionreplay.utils.DrawableUtils
7
+ import com.datadog.reactnative.sessionreplay.utils.formatAsRgba
8
+ import com.facebook.react.uimanager.Spacing
9
+ import com.facebook.react.views.view.ReactViewBackgroundDrawable
10
+
11
+ internal class ReactViewBackgroundDrawableUtils() : DrawableUtils() {
12
+ override fun resolveShapeAndBorder(
13
+ drawable: Drawable,
14
+ opacity: Float,
15
+ pixelDensity: Float
16
+ ): Pair<MobileSegment.ShapeStyle?, MobileSegment.ShapeBorder?> {
17
+ if (drawable !is ReactViewBackgroundDrawable) {
18
+ return null to null
19
+ }
20
+
21
+ val borderProps = resolveBorder(drawable, pixelDensity)
22
+ val cornerRadius = drawable
23
+ .fullBorderRadius
24
+ .toLong()
25
+ .densityNormalized(pixelDensity)
26
+
27
+ val backgroundColor = getBackgroundColor(drawable)
28
+ val colorHexString = if (backgroundColor != null) {
29
+ formatAsRgba(backgroundColor)
30
+ } else {
31
+ return null to borderProps
32
+ }
33
+
34
+ return MobileSegment.ShapeStyle(
35
+ colorHexString,
36
+ opacity,
37
+ cornerRadius
38
+ ) to borderProps
39
+ }
40
+
41
+ override fun getReactBackgroundFromDrawable(drawable: Drawable?): Drawable? {
42
+ if (drawable is ReactViewBackgroundDrawable) {
43
+ return drawable
44
+ }
45
+
46
+ if (drawable is InsetDrawable) {
47
+ return getReactBackgroundFromDrawable(drawable.drawable)
48
+ }
49
+
50
+ if (drawable is LayerDrawable) {
51
+ for (layerNumber in 0 until drawable.numberOfLayers) {
52
+ val layer = drawable.getDrawable(layerNumber)
53
+ if (layer is ReactViewBackgroundDrawable) {
54
+ return layer
55
+ }
56
+ }
57
+ }
58
+
59
+ return null
60
+ }
61
+
62
+ private fun resolveBorder(
63
+ backgroundDrawable: ReactViewBackgroundDrawable,
64
+ pixelDensity: Float
65
+ ): MobileSegment.ShapeBorder {
66
+ val borderWidth =
67
+ backgroundDrawable.fullBorderWidth.toLong().densityNormalized(pixelDensity)
68
+ val borderColor = formatAsRgba(backgroundDrawable.getBorderColor(Spacing.ALL))
69
+
70
+ return MobileSegment.ShapeBorder(
71
+ color = borderColor,
72
+ width = borderWidth
73
+ )
74
+ }
75
+
76
+ private fun getBackgroundColor(
77
+ backgroundDrawable: ReactViewBackgroundDrawable
78
+ ): Int? {
79
+ return reflectionUtils.getDeclaredField(
80
+ backgroundDrawable,
81
+ COLOR_FIELD_NAME
82
+ ) as Int?
83
+ }
84
+
85
+ private companion object {
86
+ private const val COLOR_FIELD_NAME = "mColor"
87
+ }
88
+ }
@@ -16,10 +16,10 @@ import com.facebook.react.bridge.NativeModule
16
16
  import com.facebook.react.bridge.Promise
17
17
  import com.facebook.react.bridge.ReactContext
18
18
  import com.facebook.react.uimanager.UIManagerModule
19
+ import fr.xgouchet.elmyr.annotation.BoolForgery
19
20
  import fr.xgouchet.elmyr.annotation.DoubleForgery
20
21
  import fr.xgouchet.elmyr.annotation.StringForgery
21
22
  import fr.xgouchet.elmyr.junit5.ForgeExtension
22
- import java.util.Locale
23
23
  import org.junit.jupiter.api.AfterEach
24
24
  import org.junit.jupiter.api.BeforeEach
25
25
  import org.junit.jupiter.api.Test
@@ -56,6 +56,23 @@ internal class DdSessionReplayImplementationTest {
56
56
  @Mock
57
57
  lateinit var mockUiManagerModule: UIManagerModule
58
58
 
59
+ private val imagePrivacyMap = mapOf(
60
+ "MASK_ALL" to ImagePrivacy.MASK_ALL,
61
+ "MASK_NON_BUNDLED_ONLY" to ImagePrivacy.MASK_LARGE_ONLY,
62
+ "MASK_NONE" to ImagePrivacy.MASK_NONE
63
+ )
64
+
65
+ private val touchPrivacyMap = mapOf(
66
+ "SHOW" to TouchPrivacy.SHOW,
67
+ "HIDE" to TouchPrivacy.HIDE
68
+ )
69
+
70
+ private val inputPrivacyMap = mapOf(
71
+ "MASK_ALL" to TextAndInputPrivacy.MASK_ALL,
72
+ "MASK_ALL_INPUTS" to TextAndInputPrivacy.MASK_ALL_INPUTS,
73
+ "MASK_SENSITIVE_INPUTS" to TextAndInputPrivacy.MASK_SENSITIVE_INPUTS
74
+ )
75
+
59
76
  @BeforeEach
60
77
  fun `set up`() {
61
78
  whenever(mockReactContext.getNativeModule(any<Class<NativeModule>>()))
@@ -70,33 +87,32 @@ internal class DdSessionReplayImplementationTest {
70
87
  }
71
88
 
72
89
  @Test
73
- fun `M enable session replay W privacy = ALLOW`(
74
- @DoubleForgery(min = 0.0, max = 100.0) replaySampleRate: Double,
75
- @StringForgery(regex = ".+") customEndpoint: String
76
- ) {
77
- testSessionReplayEnable("ALLOW", replaySampleRate, customEndpoint)
78
- }
79
-
80
- @Test
81
- fun `M enable session replay W privacy = MASK`(
90
+ fun `M enable session replay W random privacy settings`(
82
91
  @DoubleForgery(min = 0.0, max = 100.0) replaySampleRate: Double,
83
- @StringForgery(regex = ".+") customEndpoint: String
92
+ @StringForgery(regex = ".+") customEndpoint: String,
93
+ @BoolForgery startRecordingImmediately: Boolean
84
94
  ) {
85
- testSessionReplayEnable("MASK", replaySampleRate, customEndpoint)
86
- }
87
-
88
- @Test
89
- fun `M enable session replay W privacy = MASK_USER_INPUT`(
90
- @DoubleForgery(min = 0.0, max = 100.0) replaySampleRate: Double,
91
- @StringForgery(regex = ".+") customEndpoint: String
92
- ) {
93
- testSessionReplayEnable("MASK_USER_INPUT", replaySampleRate, customEndpoint)
95
+ val imagePrivacy = imagePrivacyMap.keys.random()
96
+ val touchPrivacy = touchPrivacyMap.keys.random()
97
+ val textAndInputPrivacy = inputPrivacyMap.keys.random()
98
+
99
+ testSessionReplayEnable(
100
+ replaySampleRate = replaySampleRate,
101
+ customEndpoint = customEndpoint,
102
+ imagePrivacy = imagePrivacy,
103
+ touchPrivacy = touchPrivacy,
104
+ textAndInputPrivacy = textAndInputPrivacy,
105
+ startRecordingImmediately = startRecordingImmediately
106
+ )
94
107
  }
95
108
 
96
109
  private fun testSessionReplayEnable(
97
- privacy: String,
98
110
  replaySampleRate: Double,
99
- customEndpoint: String
111
+ customEndpoint: String,
112
+ imagePrivacy: String,
113
+ touchPrivacy: String,
114
+ textAndInputPrivacy: String,
115
+ startRecordingImmediately: Boolean
100
116
  ) {
101
117
  // Given
102
118
  val sessionReplayConfigCaptor = argumentCaptor<SessionReplayConfiguration>()
@@ -104,8 +120,9 @@ internal class DdSessionReplayImplementationTest {
104
120
  // When
105
121
  testedSessionReplay.enable(
106
122
  replaySampleRate,
107
- privacy,
108
123
  customEndpoint,
124
+ SessionReplayPrivacySettings(imagePrivacy, touchPrivacy, textAndInputPrivacy),
125
+ startRecordingImmediately,
109
126
  mockPromise
110
127
  )
111
128
 
@@ -114,49 +131,41 @@ internal class DdSessionReplayImplementationTest {
114
131
  assertThat(sessionReplayConfigCaptor.firstValue)
115
132
  .hasFieldEqualTo("sampleRate", replaySampleRate.toFloat())
116
133
  .hasFieldEqualTo("customEndpointUrl", customEndpoint)
117
-
118
- when (privacy.lowercase(Locale.US)) {
119
- "mask_user_input" -> {
120
- assertThat(sessionReplayConfigCaptor.firstValue)
121
- .hasFieldEqualTo("textAndInputPrivacy", TextAndInputPrivacy.MASK_ALL_INPUTS)
122
- .hasFieldEqualTo("imagePrivacy", ImagePrivacy.MASK_NONE)
123
- .hasFieldEqualTo("touchPrivacy", TouchPrivacy.HIDE)
124
- }
125
- "allow" -> {
126
- assertThat(sessionReplayConfigCaptor.firstValue)
127
- .hasFieldEqualTo(
128
- "textAndInputPrivacy",
129
- TextAndInputPrivacy.MASK_SENSITIVE_INPUTS
130
- )
131
- .hasFieldEqualTo("imagePrivacy", ImagePrivacy.MASK_NONE)
132
- .hasFieldEqualTo("touchPrivacy", TouchPrivacy.SHOW)
133
- }
134
- else -> {
135
- assertThat(sessionReplayConfigCaptor.firstValue)
136
- .hasFieldEqualTo("textAndInputPrivacy", TextAndInputPrivacy.MASK_ALL)
137
- .hasFieldEqualTo("imagePrivacy", ImagePrivacy.MASK_ALL)
138
- .hasFieldEqualTo("touchPrivacy", TouchPrivacy.HIDE)
139
- }
140
- }
134
+ .hasFieldEqualTo("textAndInputPrivacy", inputPrivacyMap[textAndInputPrivacy])
135
+ .hasFieldEqualTo("imagePrivacy", imagePrivacyMap[imagePrivacy])
136
+ .hasFieldEqualTo("touchPrivacy", touchPrivacyMap[touchPrivacy])
141
137
  }
142
138
 
143
139
  @Test
144
140
  fun `M enable session replay without custom endpoint W empty string()`(
145
141
  @DoubleForgery(min = 0.0, max = 100.0) replaySampleRate: Double,
146
- // Not ALLOW nor MASK_USER_INPUT
147
- @StringForgery(regex = "^/(?!ALLOW|MASK_USER_INPUT)([a-z0-9]+)$/i") privacy: String
142
+ @BoolForgery startRecordingImmediately: Boolean
148
143
  ) {
149
144
  // Given
145
+ val imagePrivacy = imagePrivacyMap.keys.random()
146
+ val touchPrivacy = touchPrivacyMap.keys.random()
147
+ val textAndInputPrivacy = inputPrivacyMap.keys.random()
150
148
  val sessionReplayConfigCaptor = argumentCaptor<SessionReplayConfiguration>()
151
149
 
152
150
  // When
153
- testedSessionReplay.enable(replaySampleRate, privacy, "", mockPromise)
151
+ testedSessionReplay.enable(
152
+ replaySampleRate,
153
+ "",
154
+ SessionReplayPrivacySettings(
155
+ imagePrivacyLevel = imagePrivacy,
156
+ touchPrivacyLevel = touchPrivacy,
157
+ textAndInputPrivacyLevel = textAndInputPrivacy
158
+ ),
159
+ startRecordingImmediately,
160
+ mockPromise
161
+ )
154
162
 
155
163
  // Then
156
164
  verify(mockSessionReplay).enable(sessionReplayConfigCaptor.capture(), any())
157
165
  assertThat(sessionReplayConfigCaptor.firstValue)
158
166
  .hasFieldEqualTo("sampleRate", replaySampleRate.toFloat())
159
167
  .hasFieldEqualTo("privacy", SessionReplayPrivacy.MASK)
168
+ .hasFieldEqualTo("startRecordingImmediately", startRecordingImmediately)
160
169
  .doesNotHaveField("customEndpointUrl")
161
170
  }
162
171
  }
@@ -8,6 +8,7 @@ package com.datadog.reactnative.sessionreplay
8
8
 
9
9
  import com.datadog.android.api.InternalLogger
10
10
  import com.datadog.reactnative.sessionreplay.mappers.ReactEditTextMapper
11
+ import com.datadog.reactnative.sessionreplay.mappers.ReactNativeImageViewMapper
11
12
  import com.datadog.reactnative.sessionreplay.mappers.ReactTextMapper
12
13
  import com.datadog.reactnative.sessionreplay.mappers.ReactViewGroupMapper
13
14
  import com.facebook.react.bridge.NativeModule
@@ -62,15 +63,18 @@ internal class ReactNativeSessionReplayExtensionSupportTest {
62
63
  val customViewMappers = testedExtensionSupport.getCustomViewMappers()
63
64
 
64
65
  // Then
65
- assertThat(customViewMappers).hasSize(3)
66
+ assertThat(customViewMappers).hasSize(4)
66
67
 
67
68
  assertThat(customViewMappers[0].getUnsafeMapper())
68
- .isInstanceOf(ReactViewGroupMapper::class.java)
69
+ .isInstanceOf(ReactNativeImageViewMapper::class.java)
69
70
 
70
71
  assertThat(customViewMappers[1].getUnsafeMapper())
71
- .isInstanceOf(ReactTextMapper::class.java)
72
+ .isInstanceOf(ReactViewGroupMapper::class.java)
72
73
 
73
74
  assertThat(customViewMappers[2].getUnsafeMapper())
75
+ .isInstanceOf(ReactTextMapper::class.java)
76
+
77
+ assertThat(customViewMappers[3].getUnsafeMapper())
74
78
  .isInstanceOf(ReactEditTextMapper::class.java)
75
79
  }
76
80
 
@@ -15,7 +15,6 @@ import com.datadog.reactnative.sessionreplay.ReactTextPropertiesResolver.Compani
15
15
  import com.datadog.reactnative.sessionreplay.ReactTextPropertiesResolver.Companion.TEXT_ATTRIBUTES_FIELD_NAME
16
16
  import com.datadog.reactnative.sessionreplay.ShadowNodeWrapper.Companion.UI_IMPLEMENTATION_FIELD_NAME
17
17
  import com.datadog.reactnative.sessionreplay.utils.DrawableUtils
18
- import com.datadog.reactnative.sessionreplay.utils.ReactViewBackgroundDrawableUtils
19
18
  import com.datadog.reactnative.sessionreplay.utils.ReflectionUtils
20
19
  import com.datadog.reactnative.sessionreplay.utils.formatAsRgba
21
20
  import com.datadog.reactnative.tools.unit.forge.ForgeConfigurator
@@ -64,14 +63,11 @@ internal class ReactTextPropertiesResolverTest {
64
63
  @Mock
65
64
  lateinit var mockTextView: TextView
66
65
 
67
- @Mock
68
- lateinit var mockDrawableUtils: DrawableUtils
69
-
70
66
  @Mock
71
67
  lateinit var mockReactViewBackgroundDrawable: ReactViewBackgroundDrawable
72
68
 
73
69
  @Mock
74
- lateinit var mockReactViewBackgroundDrawableUtils: ReactViewBackgroundDrawableUtils
70
+ lateinit var mockDrawableUtils: DrawableUtils
75
71
 
76
72
  @Mock
77
73
  lateinit var mockShadowNodeWrapper: ShadowNodeWrapper
@@ -108,7 +104,6 @@ internal class ReactTextPropertiesResolverTest {
108
104
  testedResolver = ReactTextPropertiesResolver(
109
105
  reactContext = mockReactContext,
110
106
  uiManagerModule = mockUiManagerModule,
111
- reactViewBackgroundDrawableUtils = mockReactViewBackgroundDrawableUtils,
112
107
  drawableUtils = mockDrawableUtils,
113
108
  reflectionUtils = mockReflectionUtils
114
109
  )
@@ -145,7 +140,7 @@ internal class ReactTextPropertiesResolverTest {
145
140
  )
146
141
  ).thenReturn(mockReactViewBackgroundDrawable)
147
142
  whenever(
148
- mockReactViewBackgroundDrawableUtils.resolveShapeAndBorder(
143
+ mockDrawableUtils.resolveShapeAndBorder(
149
144
  drawable = eq(mockReactViewBackgroundDrawable),
150
145
  opacity = eq(0f),
151
146
  pixelDensity = eq(0f)
@@ -15,7 +15,6 @@ import com.datadog.android.sessionreplay.recorder.MappingContext
15
15
  import com.datadog.android.sessionreplay.recorder.SystemInformation
16
16
  import com.datadog.android.sessionreplay.utils.AsyncJobStatusCallback
17
17
  import com.datadog.reactnative.sessionreplay.utils.DrawableUtils
18
- import com.datadog.reactnative.sessionreplay.utils.ReactViewBackgroundDrawableUtils
19
18
  import com.facebook.react.views.view.ReactViewBackgroundDrawable
20
19
  import com.facebook.react.views.view.ReactViewGroup
21
20
  import fr.xgouchet.elmyr.junit5.ForgeExtension
@@ -40,7 +39,7 @@ internal class ReactViewGroupMapperTest {
40
39
  private lateinit var testedMapper: ReactViewGroupMapper
41
40
 
42
41
  @Mock
43
- private lateinit var mockReactViewBackgroundDrawableUtils: ReactViewBackgroundDrawableUtils
42
+ private lateinit var mockDrawableUtils: DrawableUtils
44
43
 
45
44
  @Mock
46
45
  private lateinit var mockReactViewGroup: ReactViewGroup
@@ -57,9 +56,6 @@ internal class ReactViewGroupMapperTest {
57
56
  @Mock
58
57
  private lateinit var mockSystemInformation: SystemInformation
59
58
 
60
- @Mock
61
- private lateinit var mockDrawableUtils: DrawableUtils
62
-
63
59
  @Mock
64
60
  private lateinit var mockReactViewBackgroundDrawable: ReactViewBackgroundDrawable
65
61
 
@@ -75,7 +71,6 @@ internal class ReactViewGroupMapperTest {
75
71
  whenever(mockSystemInformation.screenDensity).thenReturn(0f)
76
72
 
77
73
  testedMapper = ReactViewGroupMapper(
78
- reactViewBackgroundDrawableUtils = mockReactViewBackgroundDrawableUtils,
79
74
  drawableUtils = mockDrawableUtils
80
75
  )
81
76
  }
@@ -115,7 +110,7 @@ internal class ReactViewGroupMapperTest {
115
110
  )
116
111
  ).thenReturn(mockReactViewBackgroundDrawable)
117
112
  whenever(
118
- mockReactViewBackgroundDrawableUtils.resolveShapeAndBorder(
113
+ mockDrawableUtils.resolveShapeAndBorder(
119
114
  drawable = eq(mockReactViewBackgroundDrawable),
120
115
  pixelDensity = eq(0f),
121
116
  opacity = eq(0f)
@@ -6,6 +6,7 @@
6
6
 
7
7
  package com.datadog.reactnative.sessionreplay.utils
8
8
 
9
+ import ReactViewBackgroundDrawableUtils
9
10
  import android.graphics.drawable.ColorDrawable
10
11
  import android.graphics.drawable.InsetDrawable
11
12
  import android.graphics.drawable.LayerDrawable
@@ -47,7 +48,7 @@ internal class DrawableUtilsTest {
47
48
  whenever(mockLayerDrawable.numberOfLayers).thenReturn(3)
48
49
  whenever(mockLayerDrawable.getDrawable(0)).thenReturn(mockReactViewBackgroundDrawable)
49
50
 
50
- testedDrawableUtils = DrawableUtils()
51
+ testedDrawableUtils = ReactViewBackgroundDrawableUtils()
51
52
  }
52
53
 
53
54
  @Test
@@ -18,12 +18,32 @@
18
18
  RCT_EXPORT_MODULE()
19
19
 
20
20
  RCT_REMAP_METHOD(enable, withEnableReplaySampleRate:(double)replaySampleRate
21
- withDefaultPrivacyLevel:(NSString*)defaultPrivacyLevel
22
21
  withCustomEndpoint:(NSString*)customEndpoint
22
+ withImagePrivacyLevel:(NSString*)imagePrivacyLevel
23
+ withTouchPrivacyLevel:(NSString*)touchPrivacyLevel
24
+ withTextAndInputPrivacyLevel:(NSString*)textAndInputPrivacyLevel
25
+ withStartRecordingImmediately:(BOOL)startRecordingImmediately
23
26
  withResolver:(RCTPromiseResolveBlock)resolve
24
27
  withRejecter:(RCTPromiseRejectBlock)reject)
25
28
  {
26
- [self enable:replaySampleRate defaultPrivacyLevel:defaultPrivacyLevel customEndpoint:customEndpoint resolve:resolve reject:reject];
29
+ [self enable:replaySampleRate
30
+ customEndpoint:customEndpoint
31
+ imagePrivacyLevel:imagePrivacyLevel
32
+ touchPrivacyLevel:touchPrivacyLevel
33
+ textAndInputPrivacyLevel:textAndInputPrivacyLevel
34
+ startRecordingImmediately:startRecordingImmediately
35
+ resolve:resolve
36
+ reject:reject];
37
+ }
38
+
39
+ RCT_EXPORT_METHOD(startRecording:(RCTPromiseResolveBlock)resolve withRejecter:(RCTPromiseRejectBlock)reject)
40
+ {
41
+ [self startRecordingWithResolver:resolve reject:reject];
42
+ }
43
+
44
+ RCT_EXPORT_METHOD(stopRecording:(RCTPromiseResolveBlock)resolve withRejecter:(RCTPromiseRejectBlock)reject)
45
+ {
46
+ [self stopRecordingWithResolver:resolve reject:reject];
27
47
  }
28
48
 
29
49
  // Thanks to this guard, we won't compile this code when we build for the old architecture.
@@ -47,8 +67,30 @@ RCT_REMAP_METHOD(enable, withEnableReplaySampleRate:(double)replaySampleRate
47
67
  return NO;
48
68
  }
49
69
 
50
- - (void)enable:(double)replaySampleRate defaultPrivacyLevel:(NSString *)defaultPrivacyLevel customEndpoint:(NSString*)customEndpoint resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
51
- [self.ddSessionReplayImplementation enableWithReplaySampleRate:replaySampleRate defaultPrivacyLevel:defaultPrivacyLevel customEndpoint:customEndpoint resolve:resolve reject:reject];
70
+ - (void)enable:(double)replaySampleRate
71
+ customEndpoint:(NSString*)customEndpoint
72
+ imagePrivacyLevel:(NSString *)imagePrivacyLevel
73
+ touchPrivacyLevel:(NSString *)touchPrivacyLevel
74
+ textAndInputPrivacyLevel:(NSString *)textAndInputPrivacyLevel
75
+ startRecordingImmediately:(BOOL)startRecordingImmediately
76
+ resolve:(RCTPromiseResolveBlock)resolve
77
+ reject:(RCTPromiseRejectBlock)reject {
78
+ [self.ddSessionReplayImplementation enableWithReplaySampleRate:replaySampleRate
79
+ customEndpoint:customEndpoint
80
+ imagePrivacyLevel:imagePrivacyLevel
81
+ touchPrivacyLevel:touchPrivacyLevel
82
+ textAndInputPrivacyLevel:textAndInputPrivacyLevel
83
+ startRecordingImmediately:startRecordingImmediately
84
+ resolve:resolve
85
+ reject:reject];
86
+ }
87
+
88
+ - (void)startRecordingWithResolver:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
89
+ [self.ddSessionReplayImplementation startRecordingWithResolve:resolve reject:reject];
90
+ }
91
+
92
+ - (void)stopRecordingWithResolver:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
93
+ [self.ddSessionReplayImplementation stopRecordingWithResolve:resolve reject:reject];
52
94
  }
53
95
 
54
96
  @end