-
Notifications
You must be signed in to change notification settings - Fork 45
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement Android SR for ReactViewGroup
- Loading branch information
1 parent
2c52133
commit 7bed2b7
Showing
6 changed files
with
194 additions
and
0 deletions.
There are no files selected for viewing
22 changes: 22 additions & 0 deletions
22
...ession-replay/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/ColorUtils.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,22 @@ | ||
/* | ||
* 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.reactnative.sessionreplay | ||
|
||
private const val HEX_COLOR_INCLUDING_ALPHA_LENGTH: Int = 8 | ||
|
||
internal fun formatAsRgba(backgroundColor: Int): String { | ||
val colorHexString = Integer.toHexString(backgroundColor) | ||
return "#${convertArgbToRgba(colorHexString)}" | ||
} | ||
|
||
private fun convertArgbToRgba(hexString: String): String { | ||
return if (hexString.length == HEX_COLOR_INCLUDING_ALPHA_LENGTH) { | ||
hexString.substring(2, 8) + hexString.substring(0, 2) | ||
} else { | ||
hexString | ||
} | ||
} |
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
15 changes: 15 additions & 0 deletions
15
...e-session-replay/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/LongExt.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.reactnative.sessionreplay | ||
|
||
internal fun Long.convertToDensityNormalized(density: Float): Long { | ||
return if (density == 0f) { | ||
this | ||
} else { | ||
(this / density).toLong() | ||
} | ||
} |
27 changes: 27 additions & 0 deletions
27
.../kotlin/com/datadog/reactnative/sessionreplay/ReactNativeSessionReplayExtensionSupport.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,27 @@ | ||
/* | ||
* 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.reactnative.sessionreplay | ||
|
||
import android.view.View | ||
import com.datadog.android.sessionreplay.ExtensionSupport | ||
import com.datadog.android.sessionreplay.SessionReplayPrivacy | ||
import com.datadog.android.sessionreplay.internal.recorder.OptionSelectorDetector | ||
import com.datadog.android.sessionreplay.internal.recorder.mapper.WireframeMapper | ||
import com.facebook.react.views.view.ReactViewGroup | ||
|
||
internal class ReactNativeSessionReplayExtensionSupport : ExtensionSupport { | ||
|
||
override fun getCustomViewMappers(): Map<SessionReplayPrivacy, Map<Class<*>, WireframeMapper<View, *>>> { | ||
return mapOf(SessionReplayPrivacy.ALLOW to mapOf( | ||
ReactViewGroup::class.java to ReactViewGroupMapper() as WireframeMapper<View, *> | ||
)) | ||
} | ||
|
||
override fun getOptionSelectorDetectors(): List<OptionSelectorDetector> { | ||
return listOf() | ||
} | ||
} |
87 changes: 87 additions & 0 deletions
87
...lay/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/ReactViewGroupMapper.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,87 @@ | ||
/* | ||
* 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.reactnative.sessionreplay | ||
|
||
import android.annotation.SuppressLint | ||
import com.datadog.android.sessionreplay.internal.AsyncJobStatusCallback | ||
import com.datadog.android.sessionreplay.internal.recorder.MappingContext | ||
import com.datadog.android.sessionreplay.internal.recorder.mapper.BaseWireframeMapper | ||
import com.datadog.android.sessionreplay.internal.recorder.mapper.TraverseAllChildrenMapper | ||
import com.datadog.android.sessionreplay.model.MobileSegment | ||
import com.facebook.react.uimanager.Spacing | ||
import com.facebook.react.views.view.ReactViewBackgroundDrawable | ||
import com.facebook.react.views.view.ReactViewGroup | ||
|
||
internal class ReactViewGroupMapper : | ||
BaseWireframeMapper<ReactViewGroup, MobileSegment.Wireframe>(), | ||
TraverseAllChildrenMapper<ReactViewGroup, MobileSegment.Wireframe> { | ||
|
||
override fun map( | ||
view: ReactViewGroup, | ||
mappingContext: MappingContext, | ||
asyncJobStatusCallback: AsyncJobStatusCallback | ||
): List<MobileSegment.Wireframe> { | ||
val pixelDensity = mappingContext.systemInformation.screenDensity | ||
|
||
val viewGlobalBounds = resolveViewGlobalBounds( | ||
view, | ||
pixelDensity | ||
) | ||
|
||
val backgroundDrawable = view.background | ||
|
||
// view.alpha is the value of the opacity prop on the js side | ||
val opacity = view.alpha | ||
|
||
val (shapeStyle, border) = | ||
if (backgroundDrawable is ReactViewBackgroundDrawable) { | ||
resolveRNShapeStyleAndBorder( | ||
view = view, | ||
backgroundDrawable = backgroundDrawable, | ||
opacity = opacity, | ||
pixelDensity = pixelDensity | ||
) | ||
} else { | ||
backgroundDrawable?.resolveShapeStyleAndBorder(opacity) ?: (null to null) | ||
} | ||
|
||
return listOf( | ||
MobileSegment.Wireframe.ShapeWireframe( | ||
resolveViewId(view), | ||
viewGlobalBounds.x, | ||
viewGlobalBounds.y, | ||
viewGlobalBounds.width, | ||
viewGlobalBounds.height, | ||
shapeStyle = shapeStyle, | ||
border = border | ||
) | ||
) | ||
} | ||
|
||
@SuppressLint("VisibleForTests") | ||
private fun resolveRNShapeStyleAndBorder( | ||
view: ReactViewGroup, | ||
backgroundDrawable: ReactViewBackgroundDrawable, | ||
opacity: Float, | ||
pixelDensity: Float | ||
): Pair<MobileSegment.ShapeStyle, MobileSegment.ShapeBorder> { | ||
val backgroundColor = view.backgroundColor | ||
val colorHexString = formatAsRgba(backgroundColor) | ||
val cornerRadius = backgroundDrawable.fullBorderRadius.toLong().convertToDensityNormalized(pixelDensity) | ||
val borderWidth = backgroundDrawable.fullBorderWidth.toLong().convertToDensityNormalized(pixelDensity) | ||
val borderColor = formatAsRgba(backgroundDrawable.getBorderColor(Spacing.ALL)) | ||
|
||
return MobileSegment.ShapeStyle( | ||
backgroundColor = colorHexString, | ||
opacity = opacity, | ||
cornerRadius = cornerRadius | ||
) to MobileSegment.ShapeBorder( | ||
color = borderColor, | ||
width = borderWidth | ||
) | ||
} | ||
} |
42 changes: 42 additions & 0 deletions
42
...on-replay/android/src/test/kotlin/com/datadog/reactnative/sessionreplay/ColorUtilsTest.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,42 @@ | ||
/* | ||
* 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.reactnative.sessionreplay | ||
|
||
import fr.xgouchet.elmyr.junit5.ForgeExtension | ||
import org.assertj.core.api.Assertions.assertThat | ||
import org.junit.jupiter.api.Test | ||
import org.junit.jupiter.api.extension.ExtendWith | ||
import org.junit.jupiter.api.extension.Extensions | ||
import org.mockito.junit.jupiter.MockitoExtension | ||
import org.mockito.junit.jupiter.MockitoSettings | ||
import org.mockito.quality.Strictness | ||
|
||
@Extensions( | ||
ExtendWith(MockitoExtension::class), | ||
ExtendWith(ForgeExtension::class) | ||
) | ||
@MockitoSettings(strictness = Strictness.LENIENT) | ||
internal class ColorUtilsTest { | ||
|
||
@Test | ||
fun `M return without alpha W formatAsRgba { color without alpha }`() { | ||
// When | ||
val hexColor = formatAsRgba(16711680) | ||
|
||
// Then | ||
assertThat(hexColor).isEqualTo("#ff0000") | ||
} | ||
|
||
@Test | ||
fun `M resolve with alpha W formatAsRgba { color with alpha }`() { | ||
// When | ||
val hexColor = formatAsRgba(1717960806) | ||
|
||
// Then | ||
assertThat(hexColor).isEqualTo("#66006666") | ||
} | ||
} |