@@ -10,10 +10,12 @@ import androidx.compose.ui.semantics.SemanticsNode
10
10
import androidx.compose.ui.semantics.SemanticsProperties
11
11
import androidx.compose.ui.semantics.getOrNull
12
12
import androidx.compose.ui.state.ToggleableState
13
+ import com.datadog.android.api.InternalLogger
13
14
import com.datadog.android.sessionreplay.ImagePrivacy
14
15
import com.datadog.android.sessionreplay.TextAndInputPrivacy
15
16
import com.datadog.android.sessionreplay.compose.internal.data.SemanticsWireframe
16
17
import com.datadog.android.sessionreplay.compose.internal.data.UiContext
18
+ import com.datadog.android.sessionreplay.compose.internal.utils.ColorUtils
17
19
import com.datadog.android.sessionreplay.compose.internal.utils.PathUtils
18
20
import com.datadog.android.sessionreplay.compose.internal.utils.SemanticsUtils
19
21
import com.datadog.android.sessionreplay.model.MobileSegment
@@ -24,7 +26,9 @@ import com.datadog.android.sessionreplay.utils.GlobalBounds
24
26
internal class CheckboxSemanticsNodeMapper (
25
27
colorStringFormatter : ColorStringFormatter ,
26
28
private val semanticsUtils : SemanticsUtils = SemanticsUtils (),
27
- private val pathUtils : PathUtils = PathUtils ()
29
+ private val colorUtils : ColorUtils = ColorUtils (),
30
+ private val logger : InternalLogger = InternalLogger .UNBOUND ,
31
+ private val pathUtils : PathUtils = PathUtils (logger)
28
32
) : AbstractSemanticsNodeMapper(colorStringFormatter, semanticsUtils) {
29
33
30
34
override fun map (
@@ -35,9 +39,11 @@ internal class CheckboxSemanticsNodeMapper(
35
39
val globalBounds = resolveBounds(semanticsNode)
36
40
37
41
val checkableWireframes = if (parentContext.textAndInputPrivacy != TextAndInputPrivacy .MASK_SENSITIVE_INPUTS ) {
38
- resolveMaskedCheckable(
39
- semanticsNode = semanticsNode,
40
- globalBounds = globalBounds
42
+ listOf (
43
+ resolveMaskedCheckable(
44
+ semanticsNode = semanticsNode,
45
+ globalBounds = globalBounds
46
+ )
41
47
)
42
48
} else {
43
49
// Resolves checkable view regardless the state
@@ -58,9 +64,9 @@ internal class CheckboxSemanticsNodeMapper(
58
64
private fun resolveMaskedCheckable (
59
65
semanticsNode : SemanticsNode ,
60
66
globalBounds : GlobalBounds
61
- ): List < MobileSegment .Wireframe > {
67
+ ): MobileSegment .Wireframe {
62
68
// TODO RUM-5118: Decide how to display masked checkbox, Currently use old unchecked shape wireframe,
63
- return createUncheckedWireframes (
69
+ return createUncheckedState (
64
70
semanticsNode = semanticsNode,
65
71
globalBounds = globalBounds,
66
72
backgroundColor = DEFAULT_COLOR_WHITE ,
@@ -74,13 +80,14 @@ internal class CheckboxSemanticsNodeMapper(
74
80
parentContext : UiContext ,
75
81
asyncJobStatusCallback : AsyncJobStatusCallback ,
76
82
globalBounds : GlobalBounds
77
- ): List <MobileSegment .Wireframe > =
78
- if (isCheckboxChecked(semanticsNode)) {
79
- createCheckedWireframes (
83
+ ): List <MobileSegment .Wireframe > {
84
+ return if (isCheckboxChecked(semanticsNode)) {
85
+ createCheckedState (
80
86
parentContext = parentContext,
81
87
asyncJobStatusCallback = asyncJobStatusCallback,
82
88
semanticsNode = semanticsNode,
83
- globalBounds = globalBounds
89
+ globalBounds = globalBounds,
90
+ currentIndex = 0
84
91
)
85
92
} else {
86
93
val borderColor =
@@ -89,20 +96,24 @@ internal class CheckboxSemanticsNodeMapper(
89
96
convertColor(rawColor)
90
97
} ? : DEFAULT_COLOR_BLACK
91
98
92
- createUncheckedWireframes(
93
- semanticsNode = semanticsNode,
94
- globalBounds = globalBounds,
95
- backgroundColor = DEFAULT_COLOR_WHITE ,
96
- borderColor = borderColor,
97
- currentIndex = 0
99
+ listOf (
100
+ createUncheckedState(
101
+ semanticsNode = semanticsNode,
102
+ globalBounds = globalBounds,
103
+ backgroundColor = DEFAULT_COLOR_WHITE ,
104
+ borderColor = borderColor,
105
+ currentIndex = 0
106
+ )
98
107
)
99
108
}
109
+ }
100
110
101
- private fun createCheckedWireframes (
111
+ private fun createCheckedState (
102
112
parentContext : UiContext ,
103
113
asyncJobStatusCallback : AsyncJobStatusCallback ,
104
114
semanticsNode : SemanticsNode ,
105
- globalBounds : GlobalBounds
115
+ globalBounds : GlobalBounds ,
116
+ currentIndex : Int
106
117
): List <MobileSegment .Wireframe > {
107
118
val rawFillColor = semanticsUtils.resolveCheckboxFillColor(semanticsNode)
108
119
val rawCheckmarkColor = semanticsUtils.resolveCheckmarkColor(semanticsNode)
@@ -114,24 +125,25 @@ internal class CheckboxSemanticsNodeMapper(
114
125
convertColor(it)
115
126
} ? : getFallbackCheckmarkColor(DEFAULT_COLOR_WHITE )
116
127
117
- val parsedFillColor = pathUtils.parseColorSafe(fillColorRgba)
118
- val parsedCheckmarkColor = pathUtils.parseColorSafe(checkmarkColorRgba)
128
+ val parsedFillColor = colorUtils.parseColorSafe(fillColorRgba)
129
+ val parsedCheckmarkColor = colorUtils.parseColorSafe(checkmarkColorRgba)
130
+ val wireframes = mutableListOf<MobileSegment .Wireframe >()
119
131
120
132
if (parsedFillColor != null && parsedCheckmarkColor != null ) {
121
- val checkMarkBitmap = semanticsUtils
133
+ val androidPath = semanticsUtils
122
134
.resolveCheckPath(semanticsNode)?.let { checkPath ->
123
- pathUtils.convertPathToBitmap(
124
- checkPath = checkPath,
125
- fillColor = parsedFillColor,
126
- checkmarkColor = parsedCheckmarkColor
127
- )
135
+ pathUtils.asAndroidPathSafe(checkPath)
128
136
}
129
137
130
- if (checkMarkBitmap != null ) {
131
- parentContext.imageWireframeHelper.createImageWireframeByBitmap (
132
- id = resolveId(semanticsNode, 0 ),
138
+ if (androidPath != null ) {
139
+ parentContext.imageWireframeHelper.createImageWireframeByPath (
140
+ id = resolveId(semanticsNode, currentIndex ),
133
141
globalBounds = globalBounds,
134
- bitmap = checkMarkBitmap,
142
+ path = androidPath,
143
+ strokeColor = parsedCheckmarkColor,
144
+ strokeWidth = STROKE_WIDTH_DP .toInt(),
145
+ targetWidth = CHECKBOX_SIZE_DP ,
146
+ targetHeight = CHECKBOX_SIZE_DP ,
135
147
density = parentContext.density,
136
148
isContextualImage = false ,
137
149
imagePrivacy = ImagePrivacy .MASK_NONE ,
@@ -145,15 +157,24 @@ internal class CheckboxSemanticsNodeMapper(
145
157
border = MobileSegment .ShapeBorder (
146
158
color = fillColorRgba,
147
159
width = BOX_BORDER_WIDTH_DP
148
- )
160
+ ),
161
+ customResourceIdCacheKey = null
149
162
)?.let { imageWireframe ->
150
- return listOf (imageWireframe)
163
+ wireframes.add (imageWireframe)
151
164
}
152
165
}
153
166
}
154
167
168
+ if (wireframes.isNotEmpty()) {
169
+ return wireframes
170
+ }
171
+
155
172
// if we failed to create a wireframe from the path
156
- return createManualCheckedWireframe(semanticsNode, globalBounds, fillColorRgba)
173
+ return createManualCheckedWireframe(
174
+ semanticsNode = semanticsNode,
175
+ globalBounds = globalBounds,
176
+ backgroundColor = fillColorRgba
177
+ )
157
178
}
158
179
159
180
private fun createManualCheckedWireframe (
@@ -164,31 +185,29 @@ internal class CheckboxSemanticsNodeMapper(
164
185
val strokeColor = getFallbackCheckmarkColor(backgroundColor)
165
186
166
187
val wireframesList = mutableListOf<MobileSegment .Wireframe >()
167
- var index = 0
168
188
169
189
val borderColor =
170
190
semanticsUtils.resolveBorderColor(semanticsNode)
171
191
?.let { rawColor ->
172
192
convertColor(rawColor)
173
193
} ? : DEFAULT_COLOR_BLACK
174
194
175
- createUncheckedWireframes (
195
+ val background = createUncheckedState (
176
196
semanticsNode = semanticsNode,
177
197
globalBounds = globalBounds,
178
198
backgroundColor = backgroundColor,
179
199
borderColor = borderColor,
180
200
currentIndex = 0
181
- ).firstOrNull()?.let {
182
- wireframesList.add(it)
183
- index++
184
- }
201
+ )
202
+
203
+ wireframesList.add(background)
185
204
186
205
val checkmarkWidth = globalBounds.width * CHECKMARK_SIZE_FACTOR
187
206
val checkmarkHeight = globalBounds.height * CHECKMARK_SIZE_FACTOR
188
207
val xPos = globalBounds.x + ((globalBounds.width / 2 ) - (checkmarkWidth / 2 ))
189
208
val yPos = globalBounds.y + ((globalBounds.height / 2 ) - (checkmarkHeight / 2 ))
190
209
val foreground = MobileSegment .Wireframe .ShapeWireframe (
191
- id = resolveId(semanticsNode, index ),
210
+ id = resolveId(semanticsNode, 1 ),
192
211
x = xPos.toLong(),
193
212
y = yPos.toLong(),
194
213
width = checkmarkWidth.toLong(),
@@ -208,29 +227,27 @@ internal class CheckboxSemanticsNodeMapper(
208
227
return wireframesList
209
228
}
210
229
211
- private fun createUncheckedWireframes (
230
+ private fun createUncheckedState (
212
231
semanticsNode : SemanticsNode ,
213
232
globalBounds : GlobalBounds ,
214
233
backgroundColor : String ,
215
234
borderColor : String ,
216
235
currentIndex : Int
217
- ): List <MobileSegment .Wireframe > {
218
- return listOf (
219
- MobileSegment .Wireframe .ShapeWireframe (
220
- id = resolveId(semanticsNode, currentIndex),
221
- x = globalBounds.x,
222
- y = globalBounds.y,
223
- width = globalBounds.width,
224
- height = globalBounds.height,
225
- shapeStyle = MobileSegment .ShapeStyle (
226
- backgroundColor = backgroundColor,
227
- opacity = 1f ,
228
- cornerRadius = CHECKBOX_CORNER_RADIUS
229
- ),
230
- border = MobileSegment .ShapeBorder (
231
- color = borderColor,
232
- width = BOX_BORDER_WIDTH_DP
233
- )
236
+ ): MobileSegment .Wireframe {
237
+ return MobileSegment .Wireframe .ShapeWireframe (
238
+ id = resolveId(semanticsNode, currentIndex),
239
+ x = globalBounds.x,
240
+ y = globalBounds.y,
241
+ width = CHECKBOX_SIZE_DP .toLong(),
242
+ height = CHECKBOX_SIZE_DP .toLong(),
243
+ shapeStyle = MobileSegment .ShapeStyle (
244
+ backgroundColor = backgroundColor,
245
+ opacity = 1f ,
246
+ cornerRadius = CHECKBOX_CORNER_RADIUS
247
+ ),
248
+ border = MobileSegment .ShapeBorder (
249
+ color = borderColor,
250
+ width = BOX_BORDER_WIDTH_DP
234
251
)
235
252
)
236
253
}
0 commit comments