Skip to content

Commit

Permalink
Merge pull request #2448 from DataDog/marcosaia/RUM-7018/react-native…
Browse files Browse the repository at this point in the history
…-support

[RUM-7018] React Native Session Replay support
  • Loading branch information
marco-saia-datadog authored Dec 17, 2024
2 parents e4c4bcc + a12c7ad commit 9f3d758
Show file tree
Hide file tree
Showing 52 changed files with 1,071 additions and 307 deletions.
6 changes: 6 additions & 0 deletions dd-sdk-android-internal/api/apiSurface
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@ sealed class com.datadog.android.internal.telemetry.InternalTelemetryEvent
const val CREATION_SAMPLING_RATE_KEY: String
const val REPORTING_SAMPLING_RATE_KEY: String
fun ByteArray.toHexString(): String
object com.datadog.android.internal.utils.ImageViewUtils
fun resolveParentRectAbsPosition(android.view.View): android.graphics.Rect
fun calculateClipping(android.graphics.Rect, android.graphics.Rect, Float): android.graphics.Rect
fun resolveContentRectWithScaling(android.widget.ImageView, android.graphics.drawable.Drawable, android.widget.ImageView.ScaleType? = null): android.graphics.Rect
fun Int.densityNormalized(Float): Int
fun Long.densityNormalized(Float): Long
fun Throwable.loggableStackTrace(): String
annotation com.datadog.tools.annotation.NoOpImplementation
constructor(Boolean = false)
16 changes: 16 additions & 0 deletions dd-sdk-android-internal/api/dd-sdk-android-internal.api
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,22 @@ public final class com/datadog/android/internal/utils/ByteArrayExtKt {
public static final fun toHexString ([B)Ljava/lang/String;
}

public final class com/datadog/android/internal/utils/ImageViewUtils {
public static final field INSTANCE Lcom/datadog/android/internal/utils/ImageViewUtils;
public final fun calculateClipping (Landroid/graphics/Rect;Landroid/graphics/Rect;F)Landroid/graphics/Rect;
public final fun resolveContentRectWithScaling (Landroid/widget/ImageView;Landroid/graphics/drawable/Drawable;Landroid/widget/ImageView$ScaleType;)Landroid/graphics/Rect;
public static synthetic fun resolveContentRectWithScaling$default (Lcom/datadog/android/internal/utils/ImageViewUtils;Landroid/widget/ImageView;Landroid/graphics/drawable/Drawable;Landroid/widget/ImageView$ScaleType;ILjava/lang/Object;)Landroid/graphics/Rect;
public final fun resolveParentRectAbsPosition (Landroid/view/View;)Landroid/graphics/Rect;
}

public final class com/datadog/android/internal/utils/IntExtKt {
public static final fun densityNormalized (IF)I
}

public final class com/datadog/android/internal/utils/LongExtKt {
public static final fun densityNormalized (JF)J
}

public final class com/datadog/android/internal/utils/ThrowableExtKt {
public static final fun loggableStackTrace (Ljava/lang/Throwable;)Ljava/lang/String;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,25 @@
* Copyright 2016-Present Datadog, Inc.
*/

package com.datadog.android.sessionreplay.internal.utils
package com.datadog.android.internal.utils

import android.graphics.Rect
import android.graphics.drawable.Drawable
import android.view.View
import android.widget.ImageView
import com.datadog.android.sessionreplay.internal.recorder.densityNormalized
import com.datadog.android.sessionreplay.model.MobileSegment

internal object ImageViewUtils {
internal fun resolveParentRectAbsPosition(view: View): Rect {
/**
* A collection of view utility functions for resolving absolute
* positions, clipping bounds, and other useful data for
* image views operations.
*/
object ImageViewUtils {
/**
* Resolves the absolute position on the screen of the given [View].
* @param view the [View].
* @return the [Rect] representing the absolute position of the view.
*/
fun resolveParentRectAbsPosition(view: View): Rect {
val coords = IntArray(2)
// this will always have size >= 2
@Suppress("UnsafeThirdPartyFunctionCall")
Expand All @@ -31,7 +39,15 @@ internal object ImageViewUtils {
)
}

internal fun calculateClipping(parentRect: Rect, childRect: Rect, density: Float): MobileSegment.WireframeClip {
/**
* Calculates the clipping [Rect] of the given child [Rect] using its parent [Rect] and
* the screen density.
* @param parentRect the parent [Rect].
* @param childRect the child [Rect].
* @param density the screen density.
* @return the clipping [Rect].
*/
fun calculateClipping(parentRect: Rect, childRect: Rect, density: Float): Rect {
val left = if (childRect.left < parentRect.left) {
parentRect.left - childRect.left
} else {
Expand All @@ -52,18 +68,25 @@ internal object ImageViewUtils {
} else {
0
}

return MobileSegment.WireframeClip(
left = left.densityNormalized(density).toLong(),
top = top.densityNormalized(density).toLong(),
right = right.densityNormalized(density).toLong(),
bottom = bottom.densityNormalized(density).toLong()
return Rect(
left.densityNormalized(density),
top.densityNormalized(density),
right.densityNormalized(density),
bottom.densityNormalized(density)
)
}

internal fun resolveContentRectWithScaling(
/**
* Resolves the [Drawable] content [Rect] using the given [ImageView] scale type.
* @param imageView the [ImageView].
* @param drawable the [Drawable].
* @param customScaleType optional custom [ImageView.ScaleType].
* @return the resolved content [Rect].
*/
fun resolveContentRectWithScaling(
imageView: ImageView,
drawable: Drawable
drawable: Drawable,
customScaleType: ImageView.ScaleType? = null
): Rect {
val drawableWidthPx = drawable.intrinsicWidth
val drawableHeightPx = drawable.intrinsicHeight
Expand All @@ -79,7 +102,7 @@ internal object ImageViewUtils {

val resultRect: Rect

when (imageView.scaleType) {
when (customScaleType ?: imageView.scaleType) {
ImageView.ScaleType.FIT_START -> {
val contentRect = scaleRectToFitParent(parentRect, childRect)
resultRect = positionRectAtStart(parentRect, contentRect)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* Copyright 2016-Present Datadog, Inc.
*/

package com.datadog.android.sessionreplay.internal.recorder
package com.datadog.android.internal.utils

/**
* Normalizes an Int value (font size, view dimension, view position, etc.) according with the
Expand All @@ -13,7 +13,7 @@ package com.datadog.android.sessionreplay.internal.recorder
* view.height/2.
* @param density
*/
internal fun Int.densityNormalized(density: Float): Int {
fun Int.densityNormalized(density: Float): Int {
if (density == 0f) {
return this
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* Copyright 2016-Present Datadog, Inc.
*/

package com.datadog.android.sessionreplay.internal.recorder
package com.datadog.android.internal.utils

/**
* Normalizes a Long value (font size, view dimension, view position, etc.) according with the
Expand All @@ -13,7 +13,7 @@ package com.datadog.android.sessionreplay.internal.recorder
* view.height/2.
* @param density
*/
internal fun Long.densityNormalized(density: Float): Long {
fun Long.densityNormalized(density: Float): Long {
if (density == 0f) {
return this
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ internal class ChipWireframeMapper(
height = view.chipDrawable.intrinsicHeight,
usePIIPlaceholder = false,
drawable = view.chipDrawable,
customResourceIdCacheKey = null,
asyncJobStatusCallback = asyncJobStatusCallback
)
backgroundWireframe?.let {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,11 @@ import org.mockito.Mock
import org.mockito.junit.jupiter.MockitoExtension
import org.mockito.junit.jupiter.MockitoSettings
import org.mockito.kotlin.any
import org.mockito.kotlin.anyOrNull
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.eq
import org.mockito.kotlin.isNull
import org.mockito.kotlin.mock
import org.mockito.kotlin.times
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
import org.mockito.quality.Strictness
Expand Down Expand Up @@ -176,7 +176,8 @@ class ChipWireframeMapperTest {
clipping = isNull(),
shapeStyle = isNull(),
border = isNull(),
prefix = any()
prefix = any(),
customResourceIdCacheKey = anyOrNull()
)
}

Expand Down
18 changes: 11 additions & 7 deletions features/dd-sdk-android-session-replay/api/apiSurface
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,6 @@ interface com.datadog.android.sessionreplay.internal.recorder.obfuscator.StringO
fun obfuscate(String): String
companion object
fun getStringObfuscator(): StringObfuscator
class com.datadog.android.sessionreplay.internal.recorder.resources.DefaultDrawableCopier : DrawableCopier
override fun copy(android.graphics.drawable.Drawable, android.content.res.Resources): android.graphics.drawable.Drawable?
interface com.datadog.android.sessionreplay.internal.recorder.resources.DrawableCopier
fun copy(android.graphics.drawable.Drawable, android.content.res.Resources): android.graphics.drawable.Drawable?
interface com.datadog.android.sessionreplay.recorder.InteropViewCallback
fun map(android.view.View, MappingContext): List<com.datadog.android.sessionreplay.model.MobileSegment.Wireframe>
data class com.datadog.android.sessionreplay.recorder.MappingContext
Expand All @@ -75,7 +71,11 @@ interface com.datadog.android.sessionreplay.recorder.OptionSelectorDetector
data class com.datadog.android.sessionreplay.recorder.SystemInformation
constructor(com.datadog.android.sessionreplay.utils.GlobalBounds, Int = Configuration.ORIENTATION_UNDEFINED, Float, String? = null)
abstract class com.datadog.android.sessionreplay.recorder.mapper.BaseAsyncBackgroundWireframeMapper<T: android.view.View> : BaseWireframeMapper<T>
constructor(com.datadog.android.sessionreplay.utils.ViewIdentifierResolver, com.datadog.android.sessionreplay.utils.ColorStringFormatter, com.datadog.android.sessionreplay.utils.ViewBoundsResolver, com.datadog.android.sessionreplay.utils.DrawableToColorMapper)
override fun map(T, com.datadog.android.sessionreplay.recorder.MappingContext, com.datadog.android.sessionreplay.utils.AsyncJobStatusCallback, com.datadog.android.api.InternalLogger): List<com.datadog.android.sessionreplay.model.MobileSegment.Wireframe>
protected open fun resolveViewBackground(android.view.View, com.datadog.android.sessionreplay.recorder.MappingContext, com.datadog.android.sessionreplay.utils.AsyncJobStatusCallback, com.datadog.android.api.InternalLogger): com.datadog.android.sessionreplay.model.MobileSegment.Wireframe?
protected open fun resolveBackgroundAsShapeWireframe(android.view.View, com.datadog.android.sessionreplay.utils.GlobalBounds, Int, Int, com.datadog.android.sessionreplay.model.MobileSegment.ShapeStyle?): com.datadog.android.sessionreplay.model.MobileSegment.Wireframe.ShapeWireframe?
protected open fun resolveBackgroundAsImageWireframe(android.view.View, com.datadog.android.sessionreplay.utils.GlobalBounds, Int, Int, com.datadog.android.sessionreplay.recorder.MappingContext, com.datadog.android.sessionreplay.utils.AsyncJobStatusCallback): com.datadog.android.sessionreplay.model.MobileSegment.Wireframe?
companion object
open class com.datadog.android.sessionreplay.recorder.mapper.BaseViewGroupMapper<T: android.view.ViewGroup> : BaseAsyncBackgroundWireframeMapper<T>, TraverseAllChildrenMapper<T>
constructor(com.datadog.android.sessionreplay.utils.ViewIdentifierResolver, com.datadog.android.sessionreplay.utils.ColorStringFormatter, com.datadog.android.sessionreplay.utils.ViewBoundsResolver, com.datadog.android.sessionreplay.utils.DrawableToColorMapper)
Expand All @@ -88,7 +88,7 @@ class com.datadog.android.sessionreplay.recorder.mapper.EditTextMapper : TextVie
override fun resolveCapturedText(android.widget.EditText, com.datadog.android.sessionreplay.TextAndInputPrivacy, Boolean): String
companion object
open class com.datadog.android.sessionreplay.recorder.mapper.ImageViewMapper : BaseAsyncBackgroundWireframeMapper<android.widget.ImageView>
constructor(com.datadog.android.sessionreplay.utils.ViewIdentifierResolver, com.datadog.android.sessionreplay.utils.ColorStringFormatter, com.datadog.android.sessionreplay.utils.ViewBoundsResolver, com.datadog.android.sessionreplay.utils.DrawableToColorMapper)
constructor(com.datadog.android.sessionreplay.utils.ViewIdentifierResolver, com.datadog.android.sessionreplay.utils.ColorStringFormatter, com.datadog.android.sessionreplay.utils.ViewBoundsResolver, com.datadog.android.sessionreplay.utils.DrawableToColorMapper, com.datadog.android.sessionreplay.recorder.resources.DrawableCopier)
override fun map(android.widget.ImageView, com.datadog.android.sessionreplay.recorder.MappingContext, com.datadog.android.sessionreplay.utils.AsyncJobStatusCallback, com.datadog.android.api.InternalLogger): List<com.datadog.android.sessionreplay.model.MobileSegment.Wireframe>
open class com.datadog.android.sessionreplay.recorder.mapper.TextViewMapper<T: android.widget.TextView> : BaseAsyncBackgroundWireframeMapper<T>
constructor(com.datadog.android.sessionreplay.utils.ViewIdentifierResolver, com.datadog.android.sessionreplay.utils.ColorStringFormatter, com.datadog.android.sessionreplay.utils.ViewBoundsResolver, com.datadog.android.sessionreplay.utils.DrawableToColorMapper)
Expand All @@ -98,6 +98,10 @@ open class com.datadog.android.sessionreplay.recorder.mapper.TextViewMapper<T: a
interface com.datadog.android.sessionreplay.recorder.mapper.TraverseAllChildrenMapper<T: android.view.ViewGroup> : WireframeMapper<T>
interface com.datadog.android.sessionreplay.recorder.mapper.WireframeMapper<T: android.view.View>
fun map(T, com.datadog.android.sessionreplay.recorder.MappingContext, com.datadog.android.sessionreplay.utils.AsyncJobStatusCallback, com.datadog.android.api.InternalLogger): List<com.datadog.android.sessionreplay.model.MobileSegment.Wireframe>
class com.datadog.android.sessionreplay.recorder.resources.DefaultDrawableCopier : DrawableCopier
override fun copy(android.graphics.drawable.Drawable, android.content.res.Resources): android.graphics.drawable.Drawable?
interface com.datadog.android.sessionreplay.recorder.resources.DrawableCopier
fun copy(android.graphics.drawable.Drawable, android.content.res.Resources): android.graphics.drawable.Drawable?
open class com.datadog.android.sessionreplay.utils.AndroidMDrawableToColorMapper : LegacyDrawableToColorMapper
constructor(List<DrawableToColorMapper> = emptyList())
override fun resolveRippleDrawable(android.graphics.drawable.RippleDrawable, com.datadog.android.api.InternalLogger): Int?
Expand Down Expand Up @@ -131,8 +135,8 @@ data class com.datadog.android.sessionreplay.utils.GlobalBounds
constructor(Long, Long, Long, Long)
interface com.datadog.android.sessionreplay.utils.ImageWireframeHelper
fun createImageWireframeByBitmap(Long, GlobalBounds, android.graphics.Bitmap, Float, Boolean, com.datadog.android.sessionreplay.ImagePrivacy, AsyncJobStatusCallback, com.datadog.android.sessionreplay.model.MobileSegment.WireframeClip? = null, com.datadog.android.sessionreplay.model.MobileSegment.ShapeStyle? = null, com.datadog.android.sessionreplay.model.MobileSegment.ShapeBorder? = null): com.datadog.android.sessionreplay.model.MobileSegment.Wireframe?
fun createImageWireframeByDrawable(android.view.View, com.datadog.android.sessionreplay.ImagePrivacy, Int, Long, Long, Int, Int, Boolean, android.graphics.drawable.Drawable, com.datadog.android.sessionreplay.internal.recorder.resources.DrawableCopier = DefaultDrawableCopier(), AsyncJobStatusCallback, com.datadog.android.sessionreplay.model.MobileSegment.WireframeClip? = null, com.datadog.android.sessionreplay.model.MobileSegment.ShapeStyle? = null, com.datadog.android.sessionreplay.model.MobileSegment.ShapeBorder? = null, String? = DRAWABLE_CHILD_NAME): com.datadog.android.sessionreplay.model.MobileSegment.Wireframe?
fun createCompoundDrawableWireframes(android.widget.TextView, com.datadog.android.sessionreplay.recorder.MappingContext, Int, AsyncJobStatusCallback): MutableList<com.datadog.android.sessionreplay.model.MobileSegment.Wireframe>
fun createImageWireframeByDrawable(android.view.View, com.datadog.android.sessionreplay.ImagePrivacy, Int, Long, Long, Int, Int, Boolean, android.graphics.drawable.Drawable, com.datadog.android.sessionreplay.recorder.resources.DrawableCopier = DefaultDrawableCopier(), AsyncJobStatusCallback, com.datadog.android.sessionreplay.model.MobileSegment.WireframeClip? = null, com.datadog.android.sessionreplay.model.MobileSegment.ShapeStyle? = null, com.datadog.android.sessionreplay.model.MobileSegment.ShapeBorder? = null, String? = DRAWABLE_CHILD_NAME, String?): com.datadog.android.sessionreplay.model.MobileSegment.Wireframe?
fun createCompoundDrawableWireframes(android.widget.TextView, com.datadog.android.sessionreplay.recorder.MappingContext, Int, String?, AsyncJobStatusCallback): MutableList<com.datadog.android.sessionreplay.model.MobileSegment.Wireframe>
companion object
open class com.datadog.android.sessionreplay.utils.LegacyDrawableToColorMapper : DrawableToColorMapper
constructor(List<DrawableToColorMapper> = emptyList())
Expand Down
Loading

0 comments on commit 9f3d758

Please sign in to comment.