Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
6321b29
Publish new (#1)
Fiona2016 Jan 26, 2026
2852a76
Add "No-Op" modules for Session Replay, Logs, and Profiling features …
wnny-indea Feb 24, 2026
bca06c0
refactor: optimize dependency isolation and compatibility
wnny-indea Feb 12, 2026
aaa2cb5
Enable Logging, Session Replay, and Feature Flags in sample applications
wnny-indea Feb 25, 2026
a97649c
Add no-op implementations for Feature Flags and OpenFeature modules
wnny-indea Feb 25, 2026
79e91a3
Noop logs profiling session replay (#2)
wnny-indea Feb 25, 2026
89d3435
Update build configuration and test dependencies across SDK modules
wnny-indea Feb 25, 2026
cf0a2f0
Update shadowJar configuration and dependencies
wnny-indea Feb 25, 2026
568214d
Merge branch 'publish' into noop-logs-profiling-session-replay
wnny-indea Feb 26, 2026
128c7d2
Noop logs profiling session replay (#3)
wnny-indea Feb 26, 2026
d2f5e4e
Remove shadowJar artifacts from API and runtime elements
wnny-indea Feb 26, 2026
6a57afc
Remove shadowJar artifacts from API and runtime elements (#4)
wnny-indea Feb 26, 2026
6d722a7
Prevent task registration collisions by checking for existing tasks b…
wnny-indea Feb 26, 2026
a66a0d7
Merge branch 'publish' into noop-logs-profiling-session-replay
wnny-indea Feb 26, 2026
f22eebf
fix(build): resolve R8 conflicts, missing test dependencies, and JDK …
wnny-indea Feb 26, 2026
89b455a
Refactor dependency management and introduce No-Op SDK variants
wnny-indea Feb 26, 2026
e30ba51
Merge branch 'feat_noop_and_conflict' into fix_build
wnny-indea Feb 26, 2026
631aa71
Re-enable publishing and logs/session-replay features
wnny-indea Feb 26, 2026
fdff9e2
feat: Publish dd-sdk-android-dependencies artifact
wnny-indea Feb 27, 2026
cf8070a
Enable publishing for Session Replay and Logs modules
wnny-indea Feb 28, 2026
8a31449
Configure Maven Central publishing and signing for shaded dependencies
wnny-indea Feb 28, 2026
b5d4625
Merge branch 'publish' into fix_build
wnny-indea Feb 28, 2026
887cf44
Refactor Maven publishing configuration
wnny-indea Mar 1, 2026
78c4976
Merge branch 'fix_build' into fix_publish
wnny-indea Mar 1, 2026
f6cd072
Add new configuration options to SessionReplayConfiguration Builder
wnny-indea Mar 1, 2026
71a734b
Add utility interfaces and no-op implementations to Session Replay
wnny-indea Mar 1, 2026
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
45 changes: 38 additions & 7 deletions buildSrc/src/main/kotlin/com/datadog/gradle/config/MavenConfig.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,15 @@
package com.datadog.gradle.config

import com.vanniktech.maven.publish.AndroidSingleVariantLibrary
import com.vanniktech.maven.publish.JavaLibrary
import com.vanniktech.maven.publish.JavadocJar
import com.vanniktech.maven.publish.MavenPublishBaseExtension
import org.gradle.api.Project
import org.gradle.api.publish.PublishingExtension
import org.gradle.api.publish.maven.MavenPublication
import org.gradle.kotlin.dsl.configure
import org.gradle.kotlin.dsl.findByType
import org.gradle.kotlin.dsl.get
import org.gradle.plugins.signing.SigningExtension

object MavenConfig {
Expand All @@ -23,20 +27,47 @@ fun Project.publishingConfig(
projectDescription: String,
customArtifactId: String = name
) {
val projectName = name

// Apply Vanniktech plugin
pluginManager.apply("com.vanniktech.maven.publish.base")

// Configure Android Library publishing (sources + javadoc)
configure<MavenPublishBaseExtension> {
basePublishingConfig(projectDescription, customArtifactId) {
configure(
AndroidSingleVariantLibrary(
variant = "release",
sourcesJar = true,
publishJavadocJar = true
)
)
}
}

fun Project.publishingJavaConfig(
projectDescription: String,
customArtifactId: String = name,
useJavaLibraryPlatform: Boolean = true
) {
basePublishingConfig(projectDescription, customArtifactId) {
if (useJavaLibraryPlatform) {
configure(
JavaLibrary(
sourcesJar = true,
javadocJar = JavadocJar.Javadoc()
)
)
}
}
}

private fun Project.basePublishingConfig(
projectDescription: String,
customArtifactId: String,
platformConfig: MavenPublishBaseExtension.() -> Unit
) {
val projectName = name

// Apply Vanniktech plugin
pluginManager.apply("com.vanniktech.maven.publish.base")

// Configure publishing
configure<MavenPublishBaseExtension> {
platformConfig()

// Coordinates
coordinates(
Expand Down
80 changes: 8 additions & 72 deletions dd-sdk-android-dependencies/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,14 @@
* Copyright 2016-Present Datadog, Inc.
*/

import com.datadog.gradle.config.AndroidConfig
import com.datadog.gradle.config.MavenConfig
import com.vanniktech.maven.publish.MavenPublishBaseExtension
import java.util.Base64
import com.datadog.gradle.config.publishingJavaConfig

plugins {
`java-library`
id("com.gradleup.shadow")
`maven-publish`
signing
id("com.vanniktech.maven.publish.base")
}

dependencies {
Expand All @@ -38,83 +35,22 @@ tasks.shadowJar {
configurations = listOf(project.configurations.runtimeClasspath.get())
}

// Use Vanniktech plugin ONLY for Maven Central Portal repository setup (not for artifact configuration)
// This ensures the same publishToSonatype / Central Portal API is used as other modules
configure<MavenPublishBaseExtension> {
publishToMavenCentral(automaticRelease = false)
}
// Configure publishing using common helper
// useJavaLibraryPlatform = false because we manually configure the publication to use shadowJar
publishingJavaConfig(
projectDescription = "Shaded dependencies for FlashCat Android SDK",
useJavaLibraryPlatform = false
)

// Manual publication configuration with shadow jar as the artifact
publishing {
publications {
register<MavenPublication>("maven") {
groupId = MavenConfig.GROUP_ID
artifactId = project.name
version = AndroidConfig.VERSION.name

register<MavenPublication>(MavenConfig.PUBLICATION) {
artifact(tasks.shadowJar)

pom {
name.set(project.name)
description.set("Shaded dependencies for FlashCat Android SDK")
inceptionYear.set("2026")
url.set("https://github.com/flashcatcloud/fc-sdk-android/")

licenses {
license {
name.set("The Apache License, Version 2.0")
url.set("https://www.apache.org/licenses/LICENSE-2.0.txt")
distribution.set("repo")
}
}

organization {
name.set("FlashCat")
url.set("https://flashcat.cloud/")
}

developers {
developer {
id.set("flashcat")
name.set("FlashCat")
email.set("support@flashcat.cloud")
organization.set("FlashCat")
organizationUrl.set("https://flashcat.cloud/")
}
}

scm {
url.set("https://github.com/flashcatcloud/fc-sdk-android/")
connection.set("scm:git:git@github.com:flashcatcloud/fc-sdk-android.git")
developerConnection.set("scm:git:git@github.com:flashcatcloud/fc-sdk-android.git")
}
}
}
}
}

// Signing configuration (consistent with MavenConfig.publishingConfig())
signing {
val isLocalPublish = gradle.startParameter.taskNames.any {
it.contains("publishToMavenLocal", ignoreCase = true)
}
isRequired = !hasProperty("dd-skip-signing") && !isLocalPublish

val privateKey = System.getenv("GPG_PRIVATE_KEY")
val password = System.getenv("GPG_PASSWORD")

if (privateKey != null && password != null) {
val decodedKey = try {
String(Base64.getDecoder().decode(privateKey))
} catch (e: Exception) {
privateKey // Already decoded / plain text
}
useInMemoryPgpKeys(decodedKey, password)
}

sign(publishing.publications["maven"])
}

// Force the shadowJar to be the ONLY exported artifact for this module
// This prevents R8 duplicate class errors while ensuring the file exists for KSP
configurations.apiElements {
Expand Down
31 changes: 31 additions & 0 deletions features/dd-sdk-android-session-replay-noop/api/apiSurface
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ object com.datadog.android.sessionreplay.SessionReplay
fun stopRecording(com.datadog.android.api.SdkCore = Datadog.getInstance())
class com.datadog.android.sessionreplay.SessionReplayConfiguration
class Builder
constructor()
constructor(Float = 100.0f)
fun setSampleRate(Float): Builder
fun useCustomEndpoint(String): Builder
fun setPrivacy(SessionReplayPrivacy): Builder
Expand All @@ -42,6 +44,9 @@ class com.datadog.android.sessionreplay.SessionReplayConfiguration
fun setTouchPrivacy(TouchPrivacy): Builder
fun setTextAndInputPrivacy(TextAndInputPrivacy): Builder
fun setSystemRequirementsConfiguration(SystemRequirementsConfiguration): Builder
fun setDynamicOptimizationEnabled(Boolean): Builder
fun setSystemRequirements(SystemRequirementsConfiguration): Builder
fun startRecordingImmediately(Boolean): Builder
fun build(): SessionReplayConfiguration
interface com.datadog.android.sessionreplay.SessionReplayInternalCallback
fun addResourceItem(String, ByteArray, String)
Expand Down Expand Up @@ -71,6 +76,32 @@ enum com.datadog.android.sessionreplay.TouchPrivacy : PrivacyLevel
class com.datadog.android.sessionreplay._SessionReplayInternalProxy
constructor(SessionReplayConfiguration.Builder)
fun setInternalCallback(SessionReplayInternalCallback): SessionReplayConfiguration.Builder
interface com.datadog.android.sessionreplay.utils.ColorStringFormatter
fun formatColorAsHexString(Int): String
fun formatColorAndAlphaAsHexString(Int, Int): String
object com.datadog.android.sessionreplay.utils.DefaultColorStringFormatter : ColorStringFormatter
override fun formatColorAsHexString(Int): String
override fun formatColorAndAlphaAsHexString(Int, Int): String
object com.datadog.android.sessionreplay.utils.DefaultViewBoundsResolver : ViewBoundsResolver
override fun resolveViewGlobalBounds(android.view.View, Float): GlobalBounds
override fun resolveViewPaddedBounds(android.view.View, Float): GlobalBounds
object com.datadog.android.sessionreplay.utils.DefaultViewIdentifierResolver : ViewIdentifierResolver
override fun resolveViewId(android.view.View): Long
override fun resolveChildUniqueIdentifier(android.view.View, String): Long?
interface com.datadog.android.sessionreplay.utils.DrawableToColorMapper
fun mapDrawableToColor(android.graphics.drawable.Drawable, com.datadog.android.api.InternalLogger): Int?
companion object
fun getDefault(List<DrawableToColorMapper> = emptyList()): DrawableToColorMapper
class NoopDrawableToColorMapper : DrawableToColorMapper
override fun mapDrawableToColor(android.graphics.drawable.Drawable, com.datadog.android.api.InternalLogger): Int?
data class com.datadog.android.sessionreplay.utils.GlobalBounds
constructor(Long, Long, Long, Long)
interface com.datadog.android.sessionreplay.utils.ViewBoundsResolver
fun resolveViewGlobalBounds(android.view.View, Float): GlobalBounds
fun resolveViewPaddedBounds(android.view.View, Float): GlobalBounds
interface com.datadog.android.sessionreplay.utils.ViewIdentifierResolver
fun resolveViewId(android.view.View): Long
fun resolveChildUniqueIdentifier(android.view.View, String): Long?
data class com.datadog.android.sessionreplay.model.MobileSegment
constructor(Application, Session, View, kotlin.Long, kotlin.Long, kotlin.Long, kotlin.Long? = null, kotlin.Boolean? = null, Source, kotlin.collections.List<MobileRecord>)
fun toJson(): com.google.gson.JsonElement
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,14 +67,19 @@ public final class com/datadog/android/sessionreplay/SessionReplayConfiguration

public final class com/datadog/android/sessionreplay/SessionReplayConfiguration$Builder {
public fun <init> ()V
public fun <init> (F)V
public synthetic fun <init> (FILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun addExtensionSupport (Lcom/datadog/android/sessionreplay/ExtensionSupport;)Lcom/datadog/android/sessionreplay/SessionReplayConfiguration$Builder;
public final fun build ()Lcom/datadog/android/sessionreplay/SessionReplayConfiguration;
public final fun setDynamicOptimizationEnabled (Z)Lcom/datadog/android/sessionreplay/SessionReplayConfiguration$Builder;
public final fun setImagePrivacy (Lcom/datadog/android/sessionreplay/ImagePrivacy;)Lcom/datadog/android/sessionreplay/SessionReplayConfiguration$Builder;
public final fun setPrivacy (Lcom/datadog/android/sessionreplay/SessionReplayPrivacy;)Lcom/datadog/android/sessionreplay/SessionReplayConfiguration$Builder;
public final fun setSampleRate (F)Lcom/datadog/android/sessionreplay/SessionReplayConfiguration$Builder;
public final fun setSystemRequirements (Lcom/datadog/android/sessionreplay/SystemRequirementsConfiguration;)Lcom/datadog/android/sessionreplay/SessionReplayConfiguration$Builder;
public final fun setSystemRequirementsConfiguration (Lcom/datadog/android/sessionreplay/SystemRequirementsConfiguration;)Lcom/datadog/android/sessionreplay/SessionReplayConfiguration$Builder;
public final fun setTextAndInputPrivacy (Lcom/datadog/android/sessionreplay/TextAndInputPrivacy;)Lcom/datadog/android/sessionreplay/SessionReplayConfiguration$Builder;
public final fun setTouchPrivacy (Lcom/datadog/android/sessionreplay/TouchPrivacy;)Lcom/datadog/android/sessionreplay/SessionReplayConfiguration$Builder;
public final fun startRecordingImmediately (Z)Lcom/datadog/android/sessionreplay/SessionReplayConfiguration$Builder;
public final fun useCustomEndpoint (Ljava/lang/String;)Lcom/datadog/android/sessionreplay/SessionReplayConfiguration$Builder;
}

Expand Down Expand Up @@ -1434,3 +1439,68 @@ public final class com/datadog/android/sessionreplay/model/ResourceMetadata$Comp
public final fun fromJsonObject (Lcom/google/gson/JsonObject;)Lcom/datadog/android/sessionreplay/model/ResourceMetadata;
}

public abstract interface class com/datadog/android/sessionreplay/utils/ColorStringFormatter {
public abstract fun formatColorAndAlphaAsHexString (II)Ljava/lang/String;
public abstract fun formatColorAsHexString (I)Ljava/lang/String;
}

public final class com/datadog/android/sessionreplay/utils/DefaultColorStringFormatter : com/datadog/android/sessionreplay/utils/ColorStringFormatter {
public static final field INSTANCE Lcom/datadog/android/sessionreplay/utils/DefaultColorStringFormatter;
public fun formatColorAndAlphaAsHexString (II)Ljava/lang/String;
public fun formatColorAsHexString (I)Ljava/lang/String;
}

public final class com/datadog/android/sessionreplay/utils/DefaultViewBoundsResolver : com/datadog/android/sessionreplay/utils/ViewBoundsResolver {
public static final field INSTANCE Lcom/datadog/android/sessionreplay/utils/DefaultViewBoundsResolver;
public fun resolveViewGlobalBounds (Landroid/view/View;F)Lcom/datadog/android/sessionreplay/utils/GlobalBounds;
public fun resolveViewPaddedBounds (Landroid/view/View;F)Lcom/datadog/android/sessionreplay/utils/GlobalBounds;
}

public final class com/datadog/android/sessionreplay/utils/DefaultViewIdentifierResolver : com/datadog/android/sessionreplay/utils/ViewIdentifierResolver {
public static final field INSTANCE Lcom/datadog/android/sessionreplay/utils/DefaultViewIdentifierResolver;
public fun resolveChildUniqueIdentifier (Landroid/view/View;Ljava/lang/String;)Ljava/lang/Long;
public fun resolveViewId (Landroid/view/View;)J
}

public abstract interface class com/datadog/android/sessionreplay/utils/DrawableToColorMapper {
public static final field Companion Lcom/datadog/android/sessionreplay/utils/DrawableToColorMapper$Companion;
public abstract fun mapDrawableToColor (Landroid/graphics/drawable/Drawable;Lcom/datadog/android/api/InternalLogger;)Ljava/lang/Integer;
}

public final class com/datadog/android/sessionreplay/utils/DrawableToColorMapper$Companion {
public final fun getDefault (Ljava/util/List;)Lcom/datadog/android/sessionreplay/utils/DrawableToColorMapper;
public static synthetic fun getDefault$default (Lcom/datadog/android/sessionreplay/utils/DrawableToColorMapper$Companion;Ljava/util/List;ILjava/lang/Object;)Lcom/datadog/android/sessionreplay/utils/DrawableToColorMapper;
}

public final class com/datadog/android/sessionreplay/utils/DrawableToColorMapper$Companion$NoopDrawableToColorMapper : com/datadog/android/sessionreplay/utils/DrawableToColorMapper {
public fun <init> ()V
public fun mapDrawableToColor (Landroid/graphics/drawable/Drawable;Lcom/datadog/android/api/InternalLogger;)Ljava/lang/Integer;
}

public final class com/datadog/android/sessionreplay/utils/GlobalBounds {
public fun <init> (JJJJ)V
public final fun component1 ()J
public final fun component2 ()J
public final fun component3 ()J
public final fun component4 ()J
public final fun copy (JJJJ)Lcom/datadog/android/sessionreplay/utils/GlobalBounds;
public static synthetic fun copy$default (Lcom/datadog/android/sessionreplay/utils/GlobalBounds;JJJJILjava/lang/Object;)Lcom/datadog/android/sessionreplay/utils/GlobalBounds;
public fun equals (Ljava/lang/Object;)Z
public final fun getHeight ()J
public final fun getWidth ()J
public final fun getX ()J
public final fun getY ()J
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}

public abstract interface class com/datadog/android/sessionreplay/utils/ViewBoundsResolver {
public abstract fun resolveViewGlobalBounds (Landroid/view/View;F)Lcom/datadog/android/sessionreplay/utils/GlobalBounds;
public abstract fun resolveViewPaddedBounds (Landroid/view/View;F)Lcom/datadog/android/sessionreplay/utils/GlobalBounds;
}

public abstract interface class com/datadog/android/sessionreplay/utils/ViewIdentifierResolver {
public abstract fun resolveChildUniqueIdentifier (Landroid/view/View;Ljava/lang/String;)Ljava/lang/Long;
public abstract fun resolveViewId (Landroid/view/View;)J
}

Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@

package com.datadog.android.sessionreplay

import androidx.annotation.FloatRange
import com.datadog.android.api.InternalLogger

/**
* Describes configuration to be used for the Session Replay feature.
*/
Expand All @@ -16,6 +19,25 @@ class SessionReplayConfiguration internal constructor() {
* A Builder class for a [SessionReplayConfiguration].
*/
class Builder {
/**
* Calling this constructor will default to a 100% session sampling rate.
*/
constructor() : this(100.0f, InternalLogger.UNBOUND)

/**
* @param sampleRate must be a value between 0 and 100. A value of 0
* means no session will be recorded, 100 means all sessions will be recorded.
* If this value is not provided then Session Replay will default to a 100 sample rate.
*/
constructor(
@FloatRange(from = 0.0, to = 100.0) sampleRate: Float = 100.0f
) : this(sampleRate, InternalLogger.UNBOUND)

internal constructor(
@FloatRange(from = 0.0, to = 100.0) sampleRate: Float,
logger: InternalLogger
) {
}

/**
* Sets the sample rate for this feature.
Expand Down Expand Up @@ -87,6 +109,34 @@ class SessionReplayConfiguration internal constructor() {
return this
}

/**
* This option controls whether optimization is enabled or disabled for recording Session Replay data.
* By default the value is true, meaning the dynamic optimization is enabled.
*/
fun setDynamicOptimizationEnabled(dynamicOptimizationEnabled: Boolean): Builder {
return this
}

/**
* Defines the minimum system requirements for enabling the Session Replay feature.
* When [SessionReplay.enable] is invoked, the system configuration is verified against these requirements.
* If the system meets the specified criteria, Session Replay will be successfully enabled.
* If this function is not invoked, no minimum requirements will be enforced, and Session Replay will be
* enabled on all devices.
*/
fun setSystemRequirements(systemRequirementsConfiguration: SystemRequirementsConfiguration): Builder {
return this
}

/**
* Should recording start automatically (or be manually started).
* If not specified then by default it starts automatically.
* @param enabled whether recording should start automatically or not.
*/
fun startRecordingImmediately(enabled: Boolean): Builder {
return this
}

/**
* Builds a [SessionReplayConfiguration] based on the current state of this Builder.
*/
Expand Down
Loading
Loading