diff --git a/buildSrc/src/main/kotlin/com/datadog/gradle/config/MavenConfig.kt b/buildSrc/src/main/kotlin/com/datadog/gradle/config/MavenConfig.kt index 50e90e59c2..c060408506 100644 --- a/buildSrc/src/main/kotlin/com/datadog/gradle/config/MavenConfig.kt +++ b/buildSrc/src/main/kotlin/com/datadog/gradle/config/MavenConfig.kt @@ -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 { @@ -23,13 +27,7 @@ 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 { + basePublishingConfig(projectDescription, customArtifactId) { configure( AndroidSingleVariantLibrary( variant = "release", @@ -37,6 +35,39 @@ fun Project.publishingConfig( 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 { + platformConfig() // Coordinates coordinates( diff --git a/dd-sdk-android-dependencies/build.gradle.kts b/dd-sdk-android-dependencies/build.gradle.kts index a4a3e46b33..365a3c3b0c 100644 --- a/dd-sdk-android-dependencies/build.gradle.kts +++ b/dd-sdk-android-dependencies/build.gradle.kts @@ -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 { @@ -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 { - 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("maven") { - groupId = MavenConfig.GROUP_ID - artifactId = project.name - version = AndroidConfig.VERSION.name - + register(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 { diff --git a/features/dd-sdk-android-session-replay-noop/api/apiSurface b/features/dd-sdk-android-session-replay-noop/api/apiSurface index b140441956..9fd93f39a1 100644 --- a/features/dd-sdk-android-session-replay-noop/api/apiSurface +++ b/features/dd-sdk-android-session-replay-noop/api/apiSurface @@ -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 @@ -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) @@ -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 = 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) fun toJson(): com.google.gson.JsonElement diff --git a/features/dd-sdk-android-session-replay-noop/api/dd-sdk-android-session-replay-noop.api b/features/dd-sdk-android-session-replay-noop/api/dd-sdk-android-session-replay-noop.api index 5284e19175..452a7a0702 100644 --- a/features/dd-sdk-android-session-replay-noop/api/dd-sdk-android-session-replay-noop.api +++ b/features/dd-sdk-android-session-replay-noop/api/dd-sdk-android-session-replay-noop.api @@ -67,14 +67,19 @@ public final class com/datadog/android/sessionreplay/SessionReplayConfiguration public final class com/datadog/android/sessionreplay/SessionReplayConfiguration$Builder { public fun ()V + public fun (F)V + public synthetic fun (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; } @@ -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 ()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 (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 +} + diff --git a/features/dd-sdk-android-session-replay-noop/src/main/kotlin/com/datadog/android/sessionreplay/SessionReplayConfiguration.kt b/features/dd-sdk-android-session-replay-noop/src/main/kotlin/com/datadog/android/sessionreplay/SessionReplayConfiguration.kt index 52b4f2c344..ea44e39e7c 100644 --- a/features/dd-sdk-android-session-replay-noop/src/main/kotlin/com/datadog/android/sessionreplay/SessionReplayConfiguration.kt +++ b/features/dd-sdk-android-session-replay-noop/src/main/kotlin/com/datadog/android/sessionreplay/SessionReplayConfiguration.kt @@ -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. */ @@ -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. @@ -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. */ diff --git a/features/dd-sdk-android-session-replay-noop/src/main/kotlin/com/datadog/android/sessionreplay/utils/ColorStringFormatter.kt b/features/dd-sdk-android-session-replay-noop/src/main/kotlin/com/datadog/android/sessionreplay/utils/ColorStringFormatter.kt new file mode 100644 index 0000000000..46668f1283 --- /dev/null +++ b/features/dd-sdk-android-session-replay-noop/src/main/kotlin/com/datadog/android/sessionreplay/utils/ColorStringFormatter.kt @@ -0,0 +1,30 @@ +/* + * 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.android.sessionreplay.utils + +/** + * A utility interface to convert Android/JVM colors to web hexadecimal strings. + * This interface is meant for internal usage, please use it carefully. + */ +interface ColorStringFormatter { + + /** + * Converts a color as an int to a standard web hexadecimal representation, as RGBA (e.g.: #A538AFFF). + * @param color the color value (with or without alpha in the first 8 bits) + * @return new color value as a HTML formatted hexadecimal String + */ + fun formatColorAsHexString(color: Int): String + + /** + * Converts a color as an int to a standard web hexadecimal representation, as RGBA (e.g.: #A538AFFF). + * If also overrides the color's alpha channel + * @param color the color value (with or without alpha in the first 8 bits) + * @param alpha the override alpha in a [O…255] range + * @return new color value as a HTML formatted hexadecimal String + */ + fun formatColorAndAlphaAsHexString(color: Int, alpha: Int): String +} diff --git a/features/dd-sdk-android-session-replay-noop/src/main/kotlin/com/datadog/android/sessionreplay/utils/DefaultColorStringFormatter.kt b/features/dd-sdk-android-session-replay-noop/src/main/kotlin/com/datadog/android/sessionreplay/utils/DefaultColorStringFormatter.kt new file mode 100644 index 0000000000..ad4f42c7b0 --- /dev/null +++ b/features/dd-sdk-android-session-replay-noop/src/main/kotlin/com/datadog/android/sessionreplay/utils/DefaultColorStringFormatter.kt @@ -0,0 +1,24 @@ +/* + * 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.android.sessionreplay.utils + + +/** + * String utility methods needed in the Session Replay Wireframe Mappers. + * This class is meant for internal usage so please use it with careful as it might change in time. + */ +object DefaultColorStringFormatter : ColorStringFormatter { + + override fun formatColorAsHexString(color: Int): String { + // shift Android's ARGB to Web RGBA + return "" + } + + override fun formatColorAndAlphaAsHexString(color: Int, alpha: Int): String { + return "" + } +} diff --git a/features/dd-sdk-android-session-replay-noop/src/main/kotlin/com/datadog/android/sessionreplay/utils/DefaultViewBoundsResolver.kt b/features/dd-sdk-android-session-replay-noop/src/main/kotlin/com/datadog/android/sessionreplay/utils/DefaultViewBoundsResolver.kt new file mode 100644 index 0000000000..6965105a18 --- /dev/null +++ b/features/dd-sdk-android-session-replay-noop/src/main/kotlin/com/datadog/android/sessionreplay/utils/DefaultViewBoundsResolver.kt @@ -0,0 +1,24 @@ +/* + * 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.android.sessionreplay.utils + +import android.view.View + +/** + * View utility methods needed in the Session Replay Wireframe Mappers. + * This class is meant for internal usage so please use it with careful as it might change in time. + */ +object DefaultViewBoundsResolver : ViewBoundsResolver { + + override fun resolveViewGlobalBounds(view: View, screenDensity: Float): GlobalBounds { + return GlobalBounds(x = 0, y = 0, width = 0, height = 0) + } + + override fun resolveViewPaddedBounds(view: View, screenDensity: Float): GlobalBounds { + return GlobalBounds(x = 0, y = 0, width = 0, height = 0) + } +} diff --git a/features/dd-sdk-android-session-replay-noop/src/main/kotlin/com/datadog/android/sessionreplay/utils/DefaultViewIdentifierResolver.kt b/features/dd-sdk-android-session-replay-noop/src/main/kotlin/com/datadog/android/sessionreplay/utils/DefaultViewIdentifierResolver.kt new file mode 100644 index 0000000000..a0a3b95ac3 --- /dev/null +++ b/features/dd-sdk-android-session-replay-noop/src/main/kotlin/com/datadog/android/sessionreplay/utils/DefaultViewIdentifierResolver.kt @@ -0,0 +1,30 @@ +/* + * 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.android.sessionreplay.utils + +import android.view.View +import java.security.SecureRandom + +/** + * Unique Identifier Generator. + * This class is meant for internal usage so please use it with careful as it might change in time. + */ +object DefaultViewIdentifierResolver : ViewIdentifierResolver { + + internal const val DATADOG_UNIQUE_IDENTIFIER_KEY_PREFIX = "DATADOG_UNIQUE_IDENTIFIER_" + private val secureRandom = SecureRandom() + + override fun resolveViewId(view: View): Long { + // we will use the System.identityHashcode in here which returns a consistent + // value for an instance even when it is mutable + return -1 + } + + override fun resolveChildUniqueIdentifier(parent: View, childName: String): Long? { + return null + } +} diff --git a/features/dd-sdk-android-session-replay-noop/src/main/kotlin/com/datadog/android/sessionreplay/utils/DrawableToColorMapper.kt b/features/dd-sdk-android-session-replay-noop/src/main/kotlin/com/datadog/android/sessionreplay/utils/DrawableToColorMapper.kt new file mode 100644 index 0000000000..19a27066e9 --- /dev/null +++ b/features/dd-sdk-android-session-replay-noop/src/main/kotlin/com/datadog/android/sessionreplay/utils/DrawableToColorMapper.kt @@ -0,0 +1,46 @@ +/* + * 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.android.sessionreplay.utils + +import android.graphics.drawable.Drawable +import android.os.Build +import com.datadog.android.api.InternalLogger + +/** + * A utility interface to convert a [Drawable] to a meaningful color. + * This interface is meant for internal usage, please use it carefully. + */ +fun interface DrawableToColorMapper { + + /** + * Maps the drawable to its meaningful color, or null if the drawable is mostly invisible. + * @param drawable the drawable to convert + * @param internalLogger the internalLogger to report warnings + * @return the color as an Int (in 0xAARRGGBB order), or null if the drawable is mostly invisible + */ + fun mapDrawableToColor(drawable: Drawable, internalLogger: InternalLogger): Int? + + companion object { + /** + * Provides a default implementation. + * @return a default implementation based on the device API level + */ + fun getDefault(customDrawableMappers: List = emptyList()): DrawableToColorMapper { + return NoopDrawableToColorMapper() + } + + class NoopDrawableToColorMapper : DrawableToColorMapper { + override fun mapDrawableToColor( + drawable: Drawable, + internalLogger: InternalLogger + ): Int? { + return null + } + + } + } +} diff --git a/features/dd-sdk-android-session-replay-noop/src/main/kotlin/com/datadog/android/sessionreplay/utils/GlobalBounds.kt b/features/dd-sdk-android-session-replay-noop/src/main/kotlin/com/datadog/android/sessionreplay/utils/GlobalBounds.kt new file mode 100644 index 0000000000..fea677ba29 --- /dev/null +++ b/features/dd-sdk-android-session-replay-noop/src/main/kotlin/com/datadog/android/sessionreplay/utils/GlobalBounds.kt @@ -0,0 +1,25 @@ +/* + * 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.android.sessionreplay.utils + +/** + * Defines the dimension and positions in Global coordinates for a geometry. By Global we mean that + * the View position will not be relative to its parent but to the Device screen. + * These dimensions are already normalized according with the current device screen density. + * Example: if a device has a DPI = 2, the value of the dimension or position is divided by 2 to get + * a normalized value. + * @param x as the position on X axis + * @param y as the position on Y axis + * @param width as the width + * @param height as the height + */ +data class GlobalBounds( + val x: Long, + val y: Long, + val width: Long, + val height: Long +) diff --git a/features/dd-sdk-android-session-replay-noop/src/main/kotlin/com/datadog/android/sessionreplay/utils/ViewBoundsResolver.kt b/features/dd-sdk-android-session-replay-noop/src/main/kotlin/com/datadog/android/sessionreplay/utils/ViewBoundsResolver.kt new file mode 100644 index 0000000000..3fa6887dcb --- /dev/null +++ b/features/dd-sdk-android-session-replay-noop/src/main/kotlin/com/datadog/android/sessionreplay/utils/ViewBoundsResolver.kt @@ -0,0 +1,39 @@ +/* + * 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.android.sessionreplay.utils + +import android.view.View + +/** + * A utility interface to extract a [View]'s bounds relative to the device's screen, and scaled according to + * the screen's density. + * This interface is meant for internal usage, please use it carefully. + */ +interface ViewBoundsResolver { + /** + * Resolves the View bounds in device space, and normalizes them based on the screen density. + * Example: if a device has a DPI = 2, the value of the dimension or position is divided by + * 2 to get a normalized value. + * @param view the [View] + * @param screenDensity the current device screen density + * @return the computed view bounds + */ + // TODO RUM-3667 return an array of primitives here instead of creating an object. + // This method is being called too often every time we take a screen snapshot + // and we might want to avoid creating too many instances. + fun resolveViewGlobalBounds(view: View, screenDensity: Float): GlobalBounds + + /** + * Resolves the View bounds in device space excluding the padding, and normalizes them based on the screen density. + * Example: if a device has a DPI = 2, the value of the padding is divided by + * 2 to get a normalized value. + * @param view the [View] + * @param screenDensity the current device screen density + * @return the computed view padding + */ + fun resolveViewPaddedBounds(view: View, screenDensity: Float): GlobalBounds +} diff --git a/features/dd-sdk-android-session-replay-noop/src/main/kotlin/com/datadog/android/sessionreplay/utils/ViewIdentifierResolver.kt b/features/dd-sdk-android-session-replay-noop/src/main/kotlin/com/datadog/android/sessionreplay/utils/ViewIdentifierResolver.kt new file mode 100644 index 0000000000..11dec623ea --- /dev/null +++ b/features/dd-sdk-android-session-replay-noop/src/main/kotlin/com/datadog/android/sessionreplay/utils/ViewIdentifierResolver.kt @@ -0,0 +1,37 @@ +/* + * 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.android.sessionreplay.utils + +import android.view.View + +/** + * A utility interface to assign a unique id to the child of a View. + * This interface is meant for internal usage, please use it carefully. + */ +interface ViewIdentifierResolver { + + /** + * Resolves a persistent, unique id for the given view. + * @param view the view + * @return an identifier unquely mapping a view to a wireframe, allowing accurate diffs + */ + fun resolveViewId(view: View): Long + + /** + * Generates a persistent unique identifier for a virtual child view based on its unique + * name and its physical parent. The identifier will only be created once and persisted in + * the parent [View] tag to provide consistency. In case there was already a value with the + * same key in the tags and this was used by a different party we will try to use this value + * as identifier if it's a [Long], in other case we will return null. This last scenario is + * highly unlikely but we are doing this in order to safely treat possible collisions with + * client tags. + * @param parent the parent [View] of the virtual child + * @param childName the unique name of the virtual child + * @return the unique identifier as [Long] or null if the identifier could not be created + */ + fun resolveChildUniqueIdentifier(parent: View, childName: String): Long? +}