Skip to content

Commit

Permalink
Merge pull request #754 from DataDog/marcosaia/release/v2.5.0-rc0
Browse files Browse the repository at this point in the history
[Session Replay] Session Replay v2.5.0 features
  • Loading branch information
marco-saia-datadog authored Dec 19, 2024
2 parents a8a041a + 5b4fa84 commit 344af53
Show file tree
Hide file tree
Showing 18 changed files with 575 additions and 54 deletions.
8 changes: 4 additions & 4 deletions packages/core/android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -192,10 +192,10 @@ dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
compileOnly "com.squareup.okhttp3:okhttp:3.12.13"

implementation "com.datadoghq:dd-sdk-android-rum:2.14.0"
implementation "com.datadoghq:dd-sdk-android-logs:2.14.0"
implementation "com.datadoghq:dd-sdk-android-trace:2.14.0"
implementation "com.datadoghq:dd-sdk-android-webview:2.14.0"
implementation "com.datadoghq:dd-sdk-android-rum:2.16.1"
implementation "com.datadoghq:dd-sdk-android-logs:2.16.1"
implementation "com.datadoghq:dd-sdk-android-trace:2.16.1"
implementation "com.datadoghq:dd-sdk-android-webview:2.16.1"
implementation "com.google.code.gson:gson:2.10.0"
testImplementation "org.junit.platform:junit-platform-launcher:1.6.2"
testImplementation "org.junit.jupiter:junit-jupiter-api:5.6.2"
Expand Down
8 changes: 6 additions & 2 deletions 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 >= 75) {
if (reactNativeMinorVersion >= 76) {
java.srcDirs += ['src/rn76/kotlin']
} else if (reactNativeMinorVersion >= 75) {
java.srcDirs += ['src/rn75/kotlin']
} else {
java.srcDirs += ['src/rnlegacy/kotlin']
Expand All @@ -155,6 +157,7 @@ android {
buildTypes {
release {
minifyEnabled false
consumerProguardFiles 'consumer-proguard-rules.pro'
}
}
lintOptions {
Expand Down Expand Up @@ -194,7 +197,8 @@ dependencies {
api "com.facebook.react:react-android:$reactNativeVersion"
}
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation "com.datadoghq:dd-sdk-android-session-replay:2.14.0"
implementation "com.datadoghq:dd-sdk-android-session-replay:2.16.1"
implementation "com.datadoghq:dd-sdk-android-internal:2.16.1"
implementation project(path: ':datadog_mobile-react-native')

testImplementation "org.junit.platform:junit-platform-launcher:1.6.2"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
-keepclassmembers class com.facebook.drawee.drawable.RoundedBitmapDrawable {
private android.graphics.Bitmap mBitmap;
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,14 @@ import com.datadog.android.api.InternalLogger
import com.datadog.android.sessionreplay.ExtensionSupport
import com.datadog.android.sessionreplay.MapperTypeWrapper
import com.datadog.android.sessionreplay.recorder.OptionSelectorDetector
import com.datadog.android.sessionreplay.utils.DrawableToColorMapper
import com.datadog.reactnative.sessionreplay.mappers.ReactEditTextMapper
import com.datadog.reactnative.sessionreplay.mappers.ReactNativeImageViewMapper
import com.datadog.reactnative.sessionreplay.mappers.ReactTextMapper
import com.datadog.reactnative.sessionreplay.mappers.ReactViewGroupMapper
import com.facebook.react.bridge.ReactContext
import com.facebook.react.uimanager.UIManagerModule
import com.facebook.react.views.image.ReactImageView
import com.facebook.react.views.text.ReactTextView
import com.facebook.react.views.textinput.ReactEditText
import com.facebook.react.views.view.ReactViewGroup
Expand All @@ -24,21 +27,21 @@ internal class ReactNativeSessionReplayExtensionSupport(
private val reactContext: ReactContext,
private val logger: InternalLogger
) : ExtensionSupport {
override fun name(): String {
return ReactNativeSessionReplayExtensionSupport::class.java.simpleName
}

override fun getCustomViewMappers(): List<MapperTypeWrapper<*>> {
val uiManagerModule = getUiManagerModule()

return listOf(
MapperTypeWrapper(ReactImageView::class.java, ReactNativeImageViewMapper()),
MapperTypeWrapper(ReactViewGroup::class.java, ReactViewGroupMapper()),
MapperTypeWrapper(ReactTextView::class.java, ReactTextMapper(reactContext, uiManagerModule)),
MapperTypeWrapper(ReactEditText::class.java, ReactEditTextMapper(reactContext, uiManagerModule)),
)
}

override fun getOptionSelectorDetectors(): List<OptionSelectorDetector> {
return listOf()
}

@VisibleForTesting
internal fun getUiManagerModule(): UIManagerModule? {
return try {
Expand All @@ -54,6 +57,14 @@ internal class ReactNativeSessionReplayExtensionSupport(
}
}

override fun getOptionSelectorDetectors(): List<OptionSelectorDetector> {
return listOf()
}

override fun getCustomDrawableMapper(): List<DrawableToColorMapper> {
return emptyList()
}

internal companion object {
internal const val RESOLVE_UIMANAGERMODULE_ERROR = "Unable to resolve UIManagerModule"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ import ReactViewBackgroundDrawableUtils
import android.view.Gravity
import android.widget.TextView
import androidx.annotation.VisibleForTesting
import com.datadog.android.internal.utils.densityNormalized
import com.datadog.android.sessionreplay.model.MobileSegment
import com.datadog.reactnative.sessionreplay.extensions.convertToDensityNormalized
import com.datadog.reactnative.sessionreplay.utils.DrawableUtils
import com.datadog.reactnative.sessionreplay.utils.ReflectionUtils
import com.datadog.reactnative.sessionreplay.utils.formatAsRgba
Expand Down Expand Up @@ -134,7 +134,7 @@ internal class ReactTextPropertiesResolver(
val fontFamily = getFontFamily(shadowNodeWrapper)
?: textWireframe.textStyle.family
val fontSize = getFontSize(shadowNodeWrapper)
?.convertToDensityNormalized(pixelsDensity)
?.densityNormalized(pixelsDensity)
?: textWireframe.textStyle.size
val fontColor = getTextColor(shadowNodeWrapper)
?: textWireframe.textStyle.color
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
/*
* 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 android.content.res.Resources
import android.graphics.Bitmap
import android.graphics.Bitmap.Config
import android.graphics.Canvas
import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.Drawable
import android.graphics.drawable.ShapeDrawable
import android.graphics.drawable.VectorDrawable
import android.widget.ImageView
import androidx.appcompat.graphics.drawable.DrawerArrowDrawable
import com.facebook.drawee.drawable.ArrayDrawable
import com.facebook.drawee.drawable.ForwardingDrawable
import com.facebook.drawee.drawable.RoundedBitmapDrawable
import com.facebook.drawee.drawable.ScaleTypeDrawable
import com.facebook.drawee.drawable.ScalingUtils

internal fun ScaleTypeDrawable.imageViewScaleType(): ImageView.ScaleType? {
return when (scaleType) {
ScalingUtils.ScaleType.CENTER -> ImageView.ScaleType.CENTER
ScalingUtils.ScaleType.CENTER_CROP -> ImageView.ScaleType.CENTER_CROP
ScalingUtils.ScaleType.CENTER_INSIDE -> ImageView.ScaleType.CENTER_INSIDE
ScalingUtils.ScaleType.FIT_CENTER -> ImageView.ScaleType.FIT_CENTER
ScalingUtils.ScaleType.FIT_START -> ImageView.ScaleType.FIT_START
ScalingUtils.ScaleType.FIT_END -> ImageView.ScaleType.FIT_END
ScalingUtils.ScaleType.FIT_XY -> ImageView.ScaleType.FIT_XY
else -> null
}
}

internal fun ArrayDrawable.getScaleTypeDrawable(): ScaleTypeDrawable? {
for (i in 0 until numberOfLayers) {
val drawable = getDrawableOrNull(i)
if (drawable is ScaleTypeDrawable) return drawable
}

return null
}

internal fun ArrayDrawable.getDrawableOrNull(index: Int): Drawable? {
return try {
getDrawable(index)
} catch (_: IllegalArgumentException) {
null
}
}

internal fun ForwardingDrawable.tryToExtractBitmap(resources: Resources): Bitmap? {
val forwardedDrawable = drawable
return if (forwardedDrawable != null) {
forwardedDrawable.tryToExtractBitmap(resources)
} else {
toBitmapOrNull(
intrinsicWidth,
intrinsicHeight,
Config.ARGB_8888
)
}
}

internal fun RoundedBitmapDrawable.tryToExtractBitmap(): Bitmap? {
val privateBitmap = try {
val field = RoundedBitmapDrawable::class.java.getDeclaredField("mBitmap")
field.isAccessible = true
field.get(this) as? Bitmap
} catch (_: NoSuchFieldException) {
null
} catch (_: IllegalAccessException) {
null
} catch (_: Exception) {
null
}

return privateBitmap ?: toBitmapOrNull(
intrinsicWidth,
intrinsicHeight,
Config.ARGB_8888
)
}

internal fun BitmapDrawable.tryToExtractBitmap(resources: Resources): Bitmap? {
if (bitmap != null) {
return bitmap
}

if (constantState != null) {
val copy = constantState?.newDrawable(resources)
return (copy as? BitmapDrawable)?.bitmap ?: copy?.toBitmapOrNull(
intrinsicWidth,
intrinsicHeight,
Config.ARGB_8888
)
}

return null
}

internal fun ArrayDrawable.tryToExtractBitmap(resources: Resources): Bitmap? {
var width = 0
var height = 0
for (index in 0 until numberOfLayers) {
val drawable = getDrawableOrNull(index) ?: continue

if (drawable is ScaleTypeDrawable) {
return drawable.tryToExtractBitmap(resources)
}

if (drawable.intrinsicWidth * drawable.intrinsicHeight > width * height) {
width = drawable.intrinsicWidth
height = drawable.intrinsicHeight
}
}

return if (width > 0 && height > 0)
toBitmapOrNull(width, height, Config.ARGB_8888)
else
null
}

internal fun Drawable.tryToExtractBitmap(
resources: Resources
): Bitmap? {
when (this) {
is ArrayDrawable -> {
return tryToExtractBitmap(resources)
}
is ForwardingDrawable -> {
return tryToExtractBitmap(resources)
}
is RoundedBitmapDrawable -> {
return tryToExtractBitmap()
}
is BitmapDrawable -> {
return tryToExtractBitmap(resources)
}
is VectorDrawable, is ShapeDrawable, is DrawerArrowDrawable -> {
return toBitmapOrNull(
intrinsicWidth,
intrinsicHeight,
Config.ARGB_8888
)
}
else -> return null
}
}

internal fun Drawable.toBitmapOrNull(
width: Int = intrinsicWidth,
height: Int = intrinsicHeight,
config: Config? = null
): Bitmap? {
if (this is BitmapDrawable && bitmap == null) {
return null
}
return toBitmap(width, height, config)
}

internal fun Drawable.toBitmap(
width: Int = intrinsicWidth,
height: Int = intrinsicHeight,
config: Config? = null
): Bitmap {
if (this is BitmapDrawable) {
if (bitmap == null) {
return Bitmap.createBitmap(width, height, config ?: Config.ARGB_8888)
}
if (config == null || bitmap.config == config) {
// Fast-path to return original. Bitmap.createScaledBitmap will do this check, but it
// involves allocation and two jumps into native code so we perform the check ourselves.
if (width == bitmap.width && height == bitmap.height) {
return bitmap
}
return Bitmap.createScaledBitmap(bitmap, width, height, true)
}
}

val bitmap = Bitmap.createBitmap(width, height, config ?: Config.ARGB_8888)
setBounds(0, 0, width, height)
draw(Canvas(bitmap))

setBounds(bounds.left, bounds.top, bounds.right, bounds.bottom)
return bitmap
}
Loading

0 comments on commit 344af53

Please sign in to comment.