Skip to content

Commit 4a2b3e4

Browse files
authored
Merge pull request #2925 from DataDog/tvaleev/feature/release-2.26.2-backmerge
Release `2.26.2` backmerge
2 parents fd31d41 + b568b75 commit 4a2b3e4

File tree

15 files changed

+149
-13
lines changed

15 files changed

+149
-13
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,14 @@ This is the first official production version of SDK v3 containing the new archi
7575
* [MAINTENANCE] Bump Kotlin version used to `2.0.21`. See [#2766](https://github.com/DataDog/dd-sdk-android/pull/2766)
7676
* [MAINTENANCE] Bump `minSdk` version to 23. See [#2844](https://github.com/DataDog/dd-sdk-android/pull/2844)
7777

78+
# 2.26.2 / 2025-10-09
79+
80+
* [IMPROVEMENT] Extend resource handling to support multiple MIME types in RN. See [#2914](https://github.com/DataDog/dd-sdk-android/pull/2914)
81+
82+
# 2.26.1 / 2025-09-11
83+
84+
* [BUGFIX] RUM: Move session properties to `ddtags` over query parameters. See [#2812](https://github.com/DataDog/dd-sdk-android/pull/2812)
85+
7886
# 2.26.0 / 2025-08-27
7987

8088
* [FEATURE] RUM: Add battery and display attributes. See [#2815](https://github.com/DataDog/dd-sdk-android/pull/2815)

features/dd-sdk-android-session-replay/api/apiSurface

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ data class com.datadog.android.sessionreplay.SessionReplayConfiguration
3838
fun build(): SessionReplayConfiguration
3939
interface com.datadog.android.sessionreplay.SessionReplayInternalCallback
4040
fun getCurrentActivity(): android.app.Activity?
41+
fun addResourceItem(String, ByteArray, String? = null)
42+
fun setResourceQueue(SessionReplayInternalResourceQueue)
43+
interface com.datadog.android.sessionreplay.SessionReplayInternalResourceQueue
44+
fun addResourceItem(String, ByteArray, String? = null)
4145
enum com.datadog.android.sessionreplay.SessionReplayPrivacy
4246
- ALLOW
4347
- MASK

features/dd-sdk-android-session-replay/api/dd-sdk-android-session-replay.api

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,21 @@ public final class com/datadog/android/sessionreplay/SessionReplayConfiguration$
7070
}
7171

7272
public abstract interface class com/datadog/android/sessionreplay/SessionReplayInternalCallback {
73+
public abstract fun addResourceItem (Ljava/lang/String;[BLjava/lang/String;)V
7374
public abstract fun getCurrentActivity ()Landroid/app/Activity;
75+
public abstract fun setResourceQueue (Lcom/datadog/android/sessionreplay/SessionReplayInternalResourceQueue;)V
76+
}
77+
78+
public final class com/datadog/android/sessionreplay/SessionReplayInternalCallback$DefaultImpls {
79+
public static synthetic fun addResourceItem$default (Lcom/datadog/android/sessionreplay/SessionReplayInternalCallback;Ljava/lang/String;[BLjava/lang/String;ILjava/lang/Object;)V
80+
}
81+
82+
public abstract interface class com/datadog/android/sessionreplay/SessionReplayInternalResourceQueue {
83+
public abstract fun addResourceItem (Ljava/lang/String;[BLjava/lang/String;)V
84+
}
85+
86+
public final class com/datadog/android/sessionreplay/SessionReplayInternalResourceQueue$DefaultImpls {
87+
public static synthetic fun addResourceItem$default (Lcom/datadog/android/sessionreplay/SessionReplayInternalResourceQueue;Ljava/lang/String;[BLjava/lang/String;ILjava/lang/Object;)V
7488
}
7589

7690
public final class com/datadog/android/sessionreplay/SessionReplayPrivacy : java/lang/Enum {

features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/SessionReplayInternalCallback.kt

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,22 @@ interface SessionReplayInternalCallback {
2121
* that were missed because the client was initialized after the `Application.onCreate` phase.
2222
*/
2323
fun getCurrentActivity(): Activity?
24+
25+
/**
26+
* Adds a resource item to the current resource queue.
27+
*
28+
* @param identifier A unique identifier for the resource.
29+
* @param resourceData The raw content of the resource.
30+
* @param mimeType Optional MIME type describing the resource data (e.g. `"image/png"` or `"image/svg+xml"`).
31+
*/
32+
fun addResourceItem(identifier: String, resourceData: ByteArray, mimeType: String? = null)
33+
34+
/**
35+
* Sets the resource queue used by this callback. This allows resource management to also be handled
36+
* on a platform-specific level.
37+
*
38+
* @param resourceQueue The [SessionReplayInternalResourceQueue] instance that will
39+
* collect and manage resource items submitted through [addResourceItem].
40+
*/
41+
fun setResourceQueue(resourceQueue: SessionReplayInternalResourceQueue)
2442
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0.
3+
* This product includes software developed at Datadog (https://www.datadoghq.com/).
4+
* Copyright 2016-Present Datadog, Inc.
5+
*/
6+
7+
package com.datadog.android.sessionreplay
8+
9+
import com.datadog.android.lint.InternalApi
10+
11+
/**
12+
* Internal API for queuing custom resources in Session Replay.
13+
*
14+
* This interface provides access to the Session Replay resource queue, allowing clients
15+
* to manually add custom resources (e.g., SVG images, custom assets) that will be
16+
* uploaded alongside standard Session Replay data.
17+
*
18+
* Note: This is an internal API intended for advanced use cases where standard image
19+
* capture mechanisms don't apply. For regular image resources, the SDK handles
20+
* resource capture automatically.
21+
*/
22+
@InternalApi
23+
interface SessionReplayInternalResourceQueue {
24+
/**
25+
* Adds a custom resource item to the Session Replay upload queue.
26+
*
27+
* Resources are deduplicated based on their identifier. If a resource with the same
28+
* identifier has already been queued or uploaded in this session, it will not be
29+
* queued again.
30+
*
31+
* @param identifier A unique identifier for this resource. Typically an MD5 hash of
32+
* the resource content to ensure deduplication across identical resources.
33+
* @param resourceData The raw binary data of the resource to upload.
34+
* @param mimeType Optional MIME type of the resource (e.g., "image/svg+xml").
35+
* If null, the resource will be uploaded without a specific content type.
36+
*/
37+
fun addResourceItem(
38+
identifier: String,
39+
resourceData: ByteArray,
40+
mimeType: String? = null
41+
)
42+
}

features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/async/DataQueueHandler.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ import com.datadog.android.sessionreplay.recorder.SystemInformation
1212
internal interface DataQueueHandler {
1313
fun addResourceItem(
1414
identifier: String,
15-
resourceData: ByteArray
15+
resourceData: ByteArray,
16+
mimeType: String? = null
1617
): ResourceRecordedDataQueueItem?
1718
fun addTouchEventItem(
1819
pointerInteractions: List<MobileSegment.MobileRecord>

features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/async/RecordedDataQueueHandler.kt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,15 +44,17 @@ internal class RecordedDataQueueHandler(
4444
@MainThread
4545
override fun addResourceItem(
4646
identifier: String,
47-
resourceData: ByteArray
47+
resourceData: ByteArray,
48+
mimeType: String?
4849
): ResourceRecordedDataQueueItem? {
4950
val rumContextData = rumContextDataHandler.createRumContextData()
5051
?: return null
5152

5253
val item = ResourceRecordedDataQueueItem(
5354
recordedQueuedItemContext = rumContextData,
5455
identifier = identifier,
55-
resourceData = resourceData
56+
resourceData = resourceData,
57+
mimeType
5658
)
5759

5860
insertIntoRecordedDataQueue(item)

features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/async/ResourceRecordedDataQueueItem.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ import com.datadog.android.sessionreplay.internal.processor.RecordedQueuedItemCo
1111
internal class ResourceRecordedDataQueueItem(
1212
recordedQueuedItemContext: RecordedQueuedItemContext,
1313
val identifier: String,
14-
val resourceData: ByteArray
14+
val resourceData: ByteArray,
15+
val mimeType: String? = null
1516
) : RecordedDataQueueItem(recordedQueuedItemContext) {
1617

1718
override fun isValid(): Boolean {

features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/net/ResourceEvent.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ package com.datadog.android.sessionreplay.internal.net
99
internal data class ResourceEvent(
1010
val applicationId: String,
1111
val identifier: String,
12-
val resourceData: ByteArray
12+
val resourceData: ByteArray,
13+
val mimeType: String? = null
1314
) {
1415
override fun equals(other: Any?): Boolean {
1516
if (this === other) return true

features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/net/ResourceRequestBodyFactory.kt

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import com.datadog.android.sessionreplay.internal.processor.EnrichedResource.Com
1313
import com.datadog.android.sessionreplay.internal.processor.EnrichedResource.Companion.APPLICATION_KEY
1414
import com.datadog.android.sessionreplay.internal.processor.EnrichedResource.Companion.FILENAME_KEY
1515
import com.datadog.android.sessionreplay.internal.processor.EnrichedResource.Companion.ID_KEY
16+
import com.datadog.android.sessionreplay.internal.processor.EnrichedResource.Companion.MIME_TYPE
1617
import com.datadog.android.sessionreplay.internal.utils.MiscUtils
1718
import com.google.gson.JsonObject
1819
import okhttp3.MediaType.Companion.toMediaTypeOrNull
@@ -99,12 +100,18 @@ internal class ResourceRequestBodyFactory(
99100
resourceMetadata,
100101
FILENAME_KEY
101102
)
103+
val mimeType = MiscUtils.safeGetStringFromJsonObject(
104+
internalLogger,
105+
resourceMetadata,
106+
MIME_TYPE
107+
)
102108

103109
if (applicationId != null && filename != null) {
104110
ResourceEvent(
105111
applicationId = applicationId,
106112
identifier = filename,
107-
it.data
113+
it.data,
114+
mimeType = mimeType
108115
)
109116
} else {
110117
null
@@ -119,7 +126,8 @@ internal class ResourceRequestBodyFactory(
119126
resources.forEach {
120127
val filename = it.identifier
121128
val resourceData = it.resourceData
122-
addResourceRequestBody(builder, filename, resourceData)
129+
val mimeType = it.mimeType
130+
addResourceRequestBody(builder, filename, resourceData, mimeType)
123131
}
124132
}
125133

@@ -162,9 +170,14 @@ internal class ResourceRequestBodyFactory(
162170
}
163171

164172
@Suppress("TooGenericExceptionCaught")
165-
private fun addResourceRequestBody(builder: MultipartBody.Builder, filename: String, resourceData: ByteArray) {
173+
private fun addResourceRequestBody(
174+
builder: MultipartBody.Builder,
175+
filename: String,
176+
resourceData: ByteArray,
177+
mimeType: String? = null
178+
) {
166179
val body = try {
167-
resourceData.toRequestBody(CONTENT_TYPE_IMAGE)
180+
resourceData.toRequestBody(mimeType?.toMediaTypeOrNull() ?: CONTENT_TYPE_IMAGE)
168181
} catch (e: ArrayIndexOutOfBoundsException) {
169182
// this should never happen because we aren't specifying an offset
170183
internalLogger.log(

0 commit comments

Comments
 (0)