Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion packages/react-native-session-replay/android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,9 @@ android {
java.srcDirs += ['src/oldarch/kotlin']
}

if (reactNativeMinorVersion >= 76) {
if (reactNativeMinorVersion >= 79) {
java.srcDirs += ['src/rn79/kotlin']
} else if (reactNativeMinorVersion >= 76) {
java.srcDirs += ['src/rn76/kotlin']
} else if (reactNativeMinorVersion >= 75) {
java.srcDirs += ['src/rn75/kotlin']
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/*
* 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.extensions

import com.facebook.react.uimanager.LengthPercentage

internal fun LengthPercentage?.getRadius(width: Float, height: Float) = this
?.resolve(width, height)
?.let { (it.horizontal + it.vertical) / 2f }
?: 0f
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/*
* 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.
*/
import android.graphics.drawable.Drawable
import android.graphics.drawable.InsetDrawable
import android.graphics.drawable.LayerDrawable
import com.datadog.android.sessionreplay.model.MobileSegment
import com.datadog.reactnative.sessionreplay.extensions.getRadius
import com.datadog.reactnative.sessionreplay.utils.DrawableUtils
import com.datadog.reactnative.sessionreplay.utils.formatAsRgba
import com.facebook.react.common.annotations.UnstableReactNativeAPI
import com.facebook.react.uimanager.LengthPercentage
import com.facebook.react.uimanager.Spacing
import com.facebook.react.uimanager.drawable.CSSBackgroundDrawable

internal class ReactViewBackgroundDrawableUtils : DrawableUtils() {
@OptIn(UnstableReactNativeAPI::class)
override fun resolveShapeAndBorder(
drawable: Drawable,
opacity: Float,
pixelDensity: Float
): Pair<MobileSegment.ShapeStyle?, MobileSegment.ShapeBorder?> {
if (drawable !is CSSBackgroundDrawable) {
return null to null
}

val borderProps = resolveBorder(drawable, pixelDensity)
val backgroundColor = getBackgroundColor(drawable)
val colorHexString = if (backgroundColor != null) {
formatAsRgba(backgroundColor)
} else {
return null to borderProps
}

return MobileSegment.ShapeStyle(
colorHexString,
opacity,
getBorderRadius(drawable)
) to borderProps
}

@OptIn(UnstableReactNativeAPI::class)
override fun getReactBackgroundFromDrawable(drawable: Drawable?): Drawable? {
return when(drawable) {
is CSSBackgroundDrawable -> drawable
is InsetDrawable -> getReactBackgroundFromDrawable(drawable.drawable)
is LayerDrawable -> getDrawableFromLayerDrawable(drawable)
else -> null
}
}

@OptIn(UnstableReactNativeAPI::class)
private fun getDrawableFromLayerDrawable(layerDrawable: LayerDrawable): Drawable? {
for (layerNumber in 0 until layerDrawable.numberOfLayers) {
val layer = layerDrawable.getDrawable(layerNumber)
if (layer is CSSBackgroundDrawable) {
return layer
}
}
return null
}

@OptIn(UnstableReactNativeAPI::class)
private fun getBorderRadius(drawable: CSSBackgroundDrawable): Float {
val width = drawable.intrinsicWidth.toFloat()
val height = drawable.intrinsicHeight.toFloat()
val uniform = getBorderRadiusUniform(drawable)
return uniform?.getRadius(width, height) ?: 0f
}

@OptIn(UnstableReactNativeAPI::class)
private fun getBorderRadiusUniform(
drawable: CSSBackgroundDrawable
): LengthPercentage? {
return reflectionUtils.getDeclaredField(
drawable.borderRadius,
UNIFORM_FIELD_NAME
) as? LengthPercentage
}

@OptIn(UnstableReactNativeAPI::class)
private fun getBackgroundColor(
backgroundDrawable: CSSBackgroundDrawable
): Int? {
return reflectionUtils.getDeclaredField(
backgroundDrawable,
COLOR_FIELD_NAME
) as? Int
}

@OptIn(UnstableReactNativeAPI::class)
private fun resolveBorder(
backgroundDrawable: CSSBackgroundDrawable,
pixelDensity: Float
): MobileSegment.ShapeBorder {
val borderWidth = (backgroundDrawable.fullBorderWidth / pixelDensity).toLong()
val borderColor = formatAsRgba(backgroundDrawable.getBorderColor(Spacing.ALL))

return MobileSegment.ShapeBorder(
color = borderColor,
width = borderWidth
)
}

private companion object {
private const val COLOR_FIELD_NAME = "mColor"
private const val UNIFORM_FIELD_NAME = "uniform"
}
}
Loading