From 72a523bdc0dae2c603573f412e0d5ded73fcefe4 Mon Sep 17 00:00:00 2001 From: Markus Hintersteiner Date: Thu, 12 Mar 2026 20:42:34 +0100 Subject: [PATCH 1/5] Compile against JPC 1.10 --- sentry-android-replay/build.gradle.kts | 1 + .../viewhierarchy/ComposeViewHierarchyNode.kt | 9 +-- sentry-compose/api/android/sentry-compose.api | 7 ++ sentry-compose/build.gradle.kts | 75 +++++++++++++++++++ .../sentry/compose/SentryLayoutNodeHelper.kt | 58 ++++++++++++++ .../io/sentry/compose/Compose110Helper.kt | 23 ++++++ 6 files changed, 168 insertions(+), 5 deletions(-) create mode 100644 sentry-compose/src/androidMain/kotlin/io/sentry/compose/SentryLayoutNodeHelper.kt create mode 100644 sentry-compose/src/compose110/kotlin/io/sentry/compose/Compose110Helper.kt diff --git a/sentry-android-replay/build.gradle.kts b/sentry-android-replay/build.gradle.kts index 60d38c0ae0a..ffcd91c70ff 100644 --- a/sentry-android-replay/build.gradle.kts +++ b/sentry-android-replay/build.gradle.kts @@ -70,6 +70,7 @@ kotlin { explicitApi() } dependencies { api(projects.sentry) + api(projects.sentryCompose) compileOnly(libs.androidx.compose.ui.replay) implementation(kotlin(Config.kotlinStdLib, Config.kotlinStdLibVersionAndroid)) diff --git a/sentry-android-replay/src/main/java/io/sentry/android/replay/viewhierarchy/ComposeViewHierarchyNode.kt b/sentry-android-replay/src/main/java/io/sentry/android/replay/viewhierarchy/ComposeViewHierarchyNode.kt index 2e58418c3ac..ec8057a4dd9 100644 --- a/sentry-android-replay/src/main/java/io/sentry/android/replay/viewhierarchy/ComposeViewHierarchyNode.kt +++ b/sentry-android-replay/src/main/java/io/sentry/android/replay/viewhierarchy/ComposeViewHierarchyNode.kt @@ -30,6 +30,7 @@ import io.sentry.android.replay.util.toOpaque import io.sentry.android.replay.viewhierarchy.ViewHierarchyNode.GenericViewHierarchyNode import io.sentry.android.replay.viewhierarchy.ViewHierarchyNode.ImageViewHierarchyNode import io.sentry.android.replay.viewhierarchy.ViewHierarchyNode.TextViewHierarchyNode +import io.sentry.compose.SentryLayoutNodeHelper import java.lang.ref.WeakReference import java.lang.reflect.Method @@ -157,15 +158,13 @@ internal object ComposeViewHierarchyNode { shouldMask = true, isImportantForContentCapture = false, // will be set by children isVisible = - !node.outerCoordinator.isTransparent() && - visibleRect.height() > 0 && - visibleRect.width() > 0, + !SentryLayoutNodeHelper.isTransparent(node) && visibleRect.height() > 0 && visibleRect.width() > 0, visibleRect = visibleRect, ) } val isVisible = - !node.outerCoordinator.isTransparent() && + !SentryLayoutNodeHelper.isTransparent(node) && (semantics == null || !semantics.contains(SemanticsProperties.InvisibleToUser)) && visibleRect.height() > 0 && visibleRect.width() > 0 @@ -301,7 +300,7 @@ internal object ComposeViewHierarchyNode { options: SentryMaskingOptions, logger: ILogger, ) { - val children = this.children + val children = SentryLayoutNodeHelper.getChildren(this) if (children.isEmpty()) { return } diff --git a/sentry-compose/api/android/sentry-compose.api b/sentry-compose/api/android/sentry-compose.api index 3ae9af627b1..a25bb50532a 100644 --- a/sentry-compose/api/android/sentry-compose.api +++ b/sentry-compose/api/android/sentry-compose.api @@ -6,6 +6,13 @@ public final class io/sentry/compose/BuildConfig { public fun ()V } +public final class io/sentry/compose/ComposeHelper { + public static final field $stable I + public static final field INSTANCE Lio/sentry/compose/ComposeHelper; + public final fun getChildren (Landroidx/compose/ui/node/LayoutNode;)Ljava/util/List; + public final fun isTransparent (Landroidx/compose/ui/node/LayoutNode;)Z +} + public final class io/sentry/compose/SentryComposeHelperKt { public static final fun boundsInWindow (Landroidx/compose/ui/layout/LayoutCoordinates;Landroidx/compose/ui/layout/LayoutCoordinates;)Landroidx/compose/ui/geometry/Rect; } diff --git a/sentry-compose/build.gradle.kts b/sentry-compose/build.gradle.kts index 3385d0328e2..b5e6523a3b2 100644 --- a/sentry-compose/build.gradle.kts +++ b/sentry-compose/build.gradle.kts @@ -2,6 +2,7 @@ import io.gitlab.arturbosch.detekt.Detekt import org.jetbrains.dokka.gradle.DokkaTask import org.jetbrains.kotlin.gradle.dsl.JvmTarget import org.jetbrains.kotlin.gradle.dsl.KotlinVersion +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { alias(libs.plugins.kotlin.multiplatform) @@ -118,6 +119,80 @@ android { } } +// Compile Compose110Helper.kt against Compose 1.10 where internal LayoutNode accessors +// are mangled with module name "ui" (e.g. getChildren$ui()) instead of "ui_release" +val compose110Classpath by + configurations.creating { + isCanBeConsumed = false + isCanBeResolved = true + attributes { + attribute(Attribute.of("artifactType", String::class.java), "android-classes-jar") + } + } + +val compose110KotlinCompiler by + configurations.creating { + isCanBeConsumed = false + isCanBeResolved = true + } + +dependencies { + //noinspection UseTomlInstead + compose110Classpath("androidx.compose.ui:ui-android:1.10.0") + //noinspection UseTomlInstead + compose110KotlinCompiler("org.jetbrains.kotlin:kotlin-compiler-embeddable:2.2.0") +} + +val compileCompose110 by + tasks.registering(JavaExec::class) { + val sourceDir = file("src/compose110/kotlin") + val outputDir = layout.buildDirectory.dir("classes/kotlin/compose110") + val compileClasspathFiles = compose110Classpath.incoming.files + + inputs.dir(sourceDir) + inputs.files(compileClasspathFiles) + outputs.dir(outputDir) + + classpath = compose110KotlinCompiler + mainClass.set("org.jetbrains.kotlin.cli.jvm.K2JVMCompiler") + + argumentProviders.add( + CommandLineArgumentProvider { + val cp = compileClasspathFiles.files.joinToString(File.pathSeparator) + outputDir.get().asFile.mkdirs() + listOf( + sourceDir.absolutePath, + "-classpath", + cp, + "-d", + outputDir.get().asFile.absolutePath, + "-jvm-target", + "1.8", + "-language-version", + "1.9", + "-opt-in=androidx.compose.ui.ExperimentalComposeUiApi", + "-Xsuppress-version-warnings", + "-no-stdlib", + ) + } + ) + } + +// Make compose110 output available to the Android Kotlin compilation +val compose110Output = files(compileCompose110.map { it.outputs.files }) + +tasks.withType().configureEach { + if (name == "compileReleaseKotlinAndroid" || name == "compileDebugKotlinAndroid") { + dependsOn(compileCompose110) + libraries.from(compose110Output) + } +} + +// Include compose110 classes in the AAR +android.libraryVariants.all { + registerPreJavacGeneratedBytecode(project.files(compileCompose110.map { it.outputs.files })) +} + tasks.withType().configureEach { // Target version of the generated JVM bytecode. It is used for type resolution. jvmTarget = JavaVersion.VERSION_1_8.toString() diff --git a/sentry-compose/src/androidMain/kotlin/io/sentry/compose/SentryLayoutNodeHelper.kt b/sentry-compose/src/androidMain/kotlin/io/sentry/compose/SentryLayoutNodeHelper.kt new file mode 100644 index 00000000000..715699fa094 --- /dev/null +++ b/sentry-compose/src/androidMain/kotlin/io/sentry/compose/SentryLayoutNodeHelper.kt @@ -0,0 +1,58 @@ +@file:Suppress( + "INVISIBLE_MEMBER", + "INVISIBLE_REFERENCE", + "EXPOSED_PARAMETER_TYPE", + "EXPOSED_RETURN_TYPE", + "EXPOSED_FUNCTION_RETURN_TYPE", +) + +package io.sentry.compose + +import androidx.compose.ui.node.LayoutNode + +/** + * Provides access to internal LayoutNode members that are subject to Kotlin name-mangling. + * + * LayoutNode.children and LayoutNode.outerCoordinator are Kotlin `internal`, so their getters are + * mangled with the module name: getChildren$ui_release() in Compose < 1.10 vs getChildren$ui() in + * Compose >= 1.10. This class detects the version on first use and delegates to the correct + * accessor. + */ +public object SentryLayoutNodeHelper { + @Volatile private var compose110Helper: Compose110Helper? = null + @Volatile private var useCompose110: Boolean? = null + + public fun getChildren(node: LayoutNode): List { + return if (useCompose110 == true) { + compose110Helper!!.getChildren(node) + } else { + val helper = Compose110Helper() + try { + helper.getChildren(node).also { + compose110Helper = helper + useCompose110 = true + } + } catch (_: NoSuchMethodError) { + useCompose110 = false + node.children + } + } + } + + public fun isTransparent(node: LayoutNode): Boolean { + return if (useCompose110 == true) { + compose110Helper!!.getOuterCoordinator(node).isTransparent() + } else { + val helper = Compose110Helper() + try { + helper.getOuterCoordinator(node).isTransparent().also { + compose110Helper = helper + useCompose110 = true + } + } catch (_: NoSuchMethodError) { + useCompose110 = false + node.outerCoordinator.isTransparent() + } + } + } +} diff --git a/sentry-compose/src/compose110/kotlin/io/sentry/compose/Compose110Helper.kt b/sentry-compose/src/compose110/kotlin/io/sentry/compose/Compose110Helper.kt new file mode 100644 index 00000000000..c3ffde79ac3 --- /dev/null +++ b/sentry-compose/src/compose110/kotlin/io/sentry/compose/Compose110Helper.kt @@ -0,0 +1,23 @@ +@file:Suppress( + "INVISIBLE_MEMBER", + "INVISIBLE_REFERENCE", + "EXPOSED_PARAMETER_TYPE", + "EXPOSED_RETURN_TYPE", + "EXPOSED_FUNCTION_RETURN_TYPE", +) + +package io.sentry.compose + +import androidx.compose.ui.node.LayoutNode +import androidx.compose.ui.node.NodeCoordinator + +/** + * Compiled against Compose >= 1.10 where internal LayoutNode accessors are mangled with the module + * name "ui" (e.g. getChildren$ui(), getOuterCoordinator$ui()) instead of "ui_release" used in + * earlier versions. + */ +public class Compose110Helper { + public fun getChildren(node: LayoutNode): List = node.children + + public fun getOuterCoordinator(node: LayoutNode): NodeCoordinator = node.outerCoordinator +} From c86364643319a645bd09bc30509516b211116669 Mon Sep 17 00:00:00 2001 From: Sentry Github Bot Date: Thu, 12 Mar 2026 20:05:01 +0000 Subject: [PATCH 2/5] Format code --- .../android/replay/viewhierarchy/ComposeViewHierarchyNode.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sentry-android-replay/src/main/java/io/sentry/android/replay/viewhierarchy/ComposeViewHierarchyNode.kt b/sentry-android-replay/src/main/java/io/sentry/android/replay/viewhierarchy/ComposeViewHierarchyNode.kt index ec8057a4dd9..9aa21b16dc8 100644 --- a/sentry-android-replay/src/main/java/io/sentry/android/replay/viewhierarchy/ComposeViewHierarchyNode.kt +++ b/sentry-android-replay/src/main/java/io/sentry/android/replay/viewhierarchy/ComposeViewHierarchyNode.kt @@ -158,7 +158,9 @@ internal object ComposeViewHierarchyNode { shouldMask = true, isImportantForContentCapture = false, // will be set by children isVisible = - !SentryLayoutNodeHelper.isTransparent(node) && visibleRect.height() > 0 && visibleRect.width() > 0, + !SentryLayoutNodeHelper.isTransparent(node) && + visibleRect.height() > 0 && + visibleRect.width() > 0, visibleRect = visibleRect, ) } From 7400d9cabe532cca5a68901591fd8289dc13539f Mon Sep 17 00:00:00 2001 From: Markus Hintersteiner Date: Mon, 16 Mar 2026 09:19:08 +0100 Subject: [PATCH 3/5] Fix click/scroll target detection --- .../viewhierarchy/ComposeViewHierarchyNode.kt | 4 ++- sentry-compose/api/android/sentry-compose.api | 14 ++++----- .../sentry/compose/SentryLayoutNodeHelper.kt | 31 ++++++++++--------- .../gestures/ComposeGestureTargetLocator.kt | 5 +-- 4 files changed, 30 insertions(+), 24 deletions(-) diff --git a/sentry-android-replay/src/main/java/io/sentry/android/replay/viewhierarchy/ComposeViewHierarchyNode.kt b/sentry-android-replay/src/main/java/io/sentry/android/replay/viewhierarchy/ComposeViewHierarchyNode.kt index ec8057a4dd9..9aa21b16dc8 100644 --- a/sentry-android-replay/src/main/java/io/sentry/android/replay/viewhierarchy/ComposeViewHierarchyNode.kt +++ b/sentry-android-replay/src/main/java/io/sentry/android/replay/viewhierarchy/ComposeViewHierarchyNode.kt @@ -158,7 +158,9 @@ internal object ComposeViewHierarchyNode { shouldMask = true, isImportantForContentCapture = false, // will be set by children isVisible = - !SentryLayoutNodeHelper.isTransparent(node) && visibleRect.height() > 0 && visibleRect.width() > 0, + !SentryLayoutNodeHelper.isTransparent(node) && + visibleRect.height() > 0 && + visibleRect.width() > 0, visibleRect = visibleRect, ) } diff --git a/sentry-compose/api/android/sentry-compose.api b/sentry-compose/api/android/sentry-compose.api index a25bb50532a..55eca78df9d 100644 --- a/sentry-compose/api/android/sentry-compose.api +++ b/sentry-compose/api/android/sentry-compose.api @@ -6,13 +6,6 @@ public final class io/sentry/compose/BuildConfig { public fun ()V } -public final class io/sentry/compose/ComposeHelper { - public static final field $stable I - public static final field INSTANCE Lio/sentry/compose/ComposeHelper; - public final fun getChildren (Landroidx/compose/ui/node/LayoutNode;)Ljava/util/List; - public final fun isTransparent (Landroidx/compose/ui/node/LayoutNode;)Z -} - public final class io/sentry/compose/SentryComposeHelperKt { public static final fun boundsInWindow (Landroidx/compose/ui/layout/LayoutCoordinates;Landroidx/compose/ui/layout/LayoutCoordinates;)Landroidx/compose/ui/geometry/Rect; } @@ -21,6 +14,13 @@ public final class io/sentry/compose/SentryComposeTracingKt { public static final fun SentryTraced (Ljava/lang/String;Landroidx/compose/ui/Modifier;ZLkotlin/jvm/functions/Function3;Landroidx/compose/runtime/Composer;II)V } +public final class io/sentry/compose/SentryLayoutNodeHelper { + public static final field $stable I + public static final field INSTANCE Lio/sentry/compose/SentryLayoutNodeHelper; + public final fun getChildren (Landroidx/compose/ui/node/LayoutNode;)Ljava/util/List; + public final fun isTransparent (Landroidx/compose/ui/node/LayoutNode;)Z +} + public final class io/sentry/compose/SentryModifier { public static final field $stable I public static final field INSTANCE Lio/sentry/compose/SentryModifier; diff --git a/sentry-compose/src/androidMain/kotlin/io/sentry/compose/SentryLayoutNodeHelper.kt b/sentry-compose/src/androidMain/kotlin/io/sentry/compose/SentryLayoutNodeHelper.kt index 715699fa094..9d8bf8d1387 100644 --- a/sentry-compose/src/androidMain/kotlin/io/sentry/compose/SentryLayoutNodeHelper.kt +++ b/sentry-compose/src/androidMain/kotlin/io/sentry/compose/SentryLayoutNodeHelper.kt @@ -9,6 +9,7 @@ package io.sentry.compose import androidx.compose.ui.node.LayoutNode +import org.jetbrains.annotations.ApiStatus /** * Provides access to internal LayoutNode members that are subject to Kotlin name-mangling. @@ -18,20 +19,26 @@ import androidx.compose.ui.node.LayoutNode * Compose >= 1.10. This class detects the version on first use and delegates to the correct * accessor. */ +@ApiStatus.Internal public object SentryLayoutNodeHelper { @Volatile private var compose110Helper: Compose110Helper? = null @Volatile private var useCompose110: Boolean? = null + private fun getHelper(): Compose110Helper { + compose110Helper?.let { + return it + } + val helper = Compose110Helper() + compose110Helper = helper + return helper + } + public fun getChildren(node: LayoutNode): List { - return if (useCompose110 == true) { - compose110Helper!!.getChildren(node) + return if (useCompose110 == false) { + node.children } else { - val helper = Compose110Helper() try { - helper.getChildren(node).also { - compose110Helper = helper - useCompose110 = true - } + getHelper().getChildren(node).also { useCompose110 = true } } catch (_: NoSuchMethodError) { useCompose110 = false node.children @@ -40,15 +47,11 @@ public object SentryLayoutNodeHelper { } public fun isTransparent(node: LayoutNode): Boolean { - return if (useCompose110 == true) { - compose110Helper!!.getOuterCoordinator(node).isTransparent() + return if (useCompose110 == false) { + node.outerCoordinator.isTransparent() } else { - val helper = Compose110Helper() try { - helper.getOuterCoordinator(node).isTransparent().also { - compose110Helper = helper - useCompose110 = true - } + getHelper().getOuterCoordinator(node).isTransparent().also { useCompose110 = true } } catch (_: NoSuchMethodError) { useCompose110 = false node.outerCoordinator.isTransparent() diff --git a/sentry-compose/src/androidMain/kotlin/io/sentry/compose/gestures/ComposeGestureTargetLocator.kt b/sentry-compose/src/androidMain/kotlin/io/sentry/compose/gestures/ComposeGestureTargetLocator.kt index bf6a55110be..97a4ec6d491 100644 --- a/sentry-compose/src/androidMain/kotlin/io/sentry/compose/gestures/ComposeGestureTargetLocator.kt +++ b/sentry-compose/src/androidMain/kotlin/io/sentry/compose/gestures/ComposeGestureTargetLocator.kt @@ -52,11 +52,12 @@ public class ComposeGestureTargetLocator(private val logger: ILogger) : GestureT // the last known tag when iterating the node tree var lastKnownTag: String? = null + var isClickable = false + var isScrollable = false + while (!queue.isEmpty()) { val node = queue.poll() ?: continue if (node.isPlaced && layoutNodeBoundsContain(rootLayoutNode, node, x, y)) { - var isClickable = false - var isScrollable = false val modifiers = node.getModifierInfo() for (index in modifiers.indices) { From 0e2a446d608b700469c9753b081088f467e34da4 Mon Sep 17 00:00:00 2001 From: Markus Hintersteiner Date: Mon, 16 Mar 2026 09:31:01 +0100 Subject: [PATCH 4/5] Update Changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 54dff9fef7f..1e90e1a1451 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### Fixes - Android: Remove the dependency on protobuf-lite for tombstones ([#5157](https://github.com/getsentry/sentry-java/pull/5157)) +- Support masking/unmasking and click/scroll detection for Jetpack Compose 1.10+ ([#5189](https://github.com/getsentry/sentry-java/pull/5189)) ### Features From d9d89d92cfd10264a0efd9659c61c4ae32aa314d Mon Sep 17 00:00:00 2001 From: Markus Hintersteiner Date: Mon, 16 Mar 2026 12:17:52 +0100 Subject: [PATCH 5/5] Move changes into replay module --- sentry-android-replay/build.gradle.kts | 76 ++++++++++++++++++- .../replay/viewhierarchy}/Compose110Helper.kt | 2 +- .../viewhierarchy/ComposeViewHierarchyNode.kt | 1 - .../viewhierarchy}/SentryLayoutNodeHelper.kt | 10 +-- sentry-compose/api/android/sentry-compose.api | 7 -- sentry-compose/build.gradle.kts | 75 ------------------ 6 files changed, 80 insertions(+), 91 deletions(-) rename {sentry-compose/src/compose110/kotlin/io/sentry/compose => sentry-android-replay/src/compose110/kotlin/io/sentry/android/replay/viewhierarchy}/Compose110Helper.kt (93%) rename {sentry-compose/src/androidMain/kotlin/io/sentry/compose => sentry-android-replay/src/main/java/io/sentry/android/replay/viewhierarchy}/SentryLayoutNodeHelper.kt (85%) diff --git a/sentry-android-replay/build.gradle.kts b/sentry-android-replay/build.gradle.kts index ffcd91c70ff..97a80aac9e2 100644 --- a/sentry-android-replay/build.gradle.kts +++ b/sentry-android-replay/build.gradle.kts @@ -1,5 +1,6 @@ import io.gitlab.arturbosch.detekt.Detekt import org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { id("com.android.library") @@ -70,7 +71,6 @@ kotlin { explicitApi() } dependencies { api(projects.sentry) - api(projects.sentryCompose) compileOnly(libs.androidx.compose.ui.replay) implementation(kotlin(Config.kotlinStdLib, Config.kotlinStdLibVersionAndroid)) @@ -92,6 +92,80 @@ dependencies { testImplementation(libs.coil.compose) } +// Compile Compose110Helper.kt against Compose 1.10 where internal LayoutNode accessors +// are mangled with module name "ui" (e.g. getChildren$ui()) instead of "ui_release" +val compose110Classpath by + configurations.creating { + isCanBeConsumed = false + isCanBeResolved = true + attributes { + attribute(Attribute.of("artifactType", String::class.java), "android-classes-jar") + } + } + +val compose110KotlinCompiler by + configurations.creating { + isCanBeConsumed = false + isCanBeResolved = true + } + +dependencies { + //noinspection UseTomlInstead + compose110Classpath("androidx.compose.ui:ui-android:1.10.0") + //noinspection UseTomlInstead + compose110KotlinCompiler("org.jetbrains.kotlin:kotlin-compiler-embeddable:2.2.0") +} + +val compileCompose110 by + tasks.registering(JavaExec::class) { + val sourceDir = file("src/compose110/kotlin") + val outputDir = layout.buildDirectory.dir("classes/kotlin/compose110") + val compileClasspathFiles = compose110Classpath.incoming.files + + inputs.dir(sourceDir) + inputs.files(compileClasspathFiles) + outputs.dir(outputDir) + + classpath = compose110KotlinCompiler + mainClass.set("org.jetbrains.kotlin.cli.jvm.K2JVMCompiler") + + argumentProviders.add( + CommandLineArgumentProvider { + val cp = compileClasspathFiles.files.joinToString(File.pathSeparator) + outputDir.get().asFile.mkdirs() + listOf( + sourceDir.absolutePath, + "-classpath", + cp, + "-d", + outputDir.get().asFile.absolutePath, + "-jvm-target", + "1.8", + "-language-version", + "1.9", + "-opt-in=androidx.compose.ui.ExperimentalComposeUiApi", + "-Xsuppress-version-warnings", + "-no-stdlib", + ) + } + ) + } + +// Make compose110 output available to the Android Kotlin compilation +val compose110Output = files(compileCompose110.map { it.outputs.files }) + +tasks.withType().configureEach { + if (name == "compileReleaseKotlin" || name == "compileDebugKotlin") { + dependsOn(compileCompose110) + libraries.from(compose110Output) + } +} + +// Include compose110 classes in the AAR +android.libraryVariants.all { + registerPreJavacGeneratedBytecode(project.files(compileCompose110.map { it.outputs.files })) +} + tasks.withType().configureEach { // Target version of the generated JVM bytecode. It is used for type resolution. jvmTarget = JavaVersion.VERSION_1_8.toString() diff --git a/sentry-compose/src/compose110/kotlin/io/sentry/compose/Compose110Helper.kt b/sentry-android-replay/src/compose110/kotlin/io/sentry/android/replay/viewhierarchy/Compose110Helper.kt similarity index 93% rename from sentry-compose/src/compose110/kotlin/io/sentry/compose/Compose110Helper.kt rename to sentry-android-replay/src/compose110/kotlin/io/sentry/android/replay/viewhierarchy/Compose110Helper.kt index c3ffde79ac3..f25e8d6acd5 100644 --- a/sentry-compose/src/compose110/kotlin/io/sentry/compose/Compose110Helper.kt +++ b/sentry-android-replay/src/compose110/kotlin/io/sentry/android/replay/viewhierarchy/Compose110Helper.kt @@ -6,7 +6,7 @@ "EXPOSED_FUNCTION_RETURN_TYPE", ) -package io.sentry.compose +package io.sentry.android.replay.viewhierarchy import androidx.compose.ui.node.LayoutNode import androidx.compose.ui.node.NodeCoordinator diff --git a/sentry-android-replay/src/main/java/io/sentry/android/replay/viewhierarchy/ComposeViewHierarchyNode.kt b/sentry-android-replay/src/main/java/io/sentry/android/replay/viewhierarchy/ComposeViewHierarchyNode.kt index 9aa21b16dc8..cdb1be8efa1 100644 --- a/sentry-android-replay/src/main/java/io/sentry/android/replay/viewhierarchy/ComposeViewHierarchyNode.kt +++ b/sentry-android-replay/src/main/java/io/sentry/android/replay/viewhierarchy/ComposeViewHierarchyNode.kt @@ -30,7 +30,6 @@ import io.sentry.android.replay.util.toOpaque import io.sentry.android.replay.viewhierarchy.ViewHierarchyNode.GenericViewHierarchyNode import io.sentry.android.replay.viewhierarchy.ViewHierarchyNode.ImageViewHierarchyNode import io.sentry.android.replay.viewhierarchy.ViewHierarchyNode.TextViewHierarchyNode -import io.sentry.compose.SentryLayoutNodeHelper import java.lang.ref.WeakReference import java.lang.reflect.Method diff --git a/sentry-compose/src/androidMain/kotlin/io/sentry/compose/SentryLayoutNodeHelper.kt b/sentry-android-replay/src/main/java/io/sentry/android/replay/viewhierarchy/SentryLayoutNodeHelper.kt similarity index 85% rename from sentry-compose/src/androidMain/kotlin/io/sentry/compose/SentryLayoutNodeHelper.kt rename to sentry-android-replay/src/main/java/io/sentry/android/replay/viewhierarchy/SentryLayoutNodeHelper.kt index 9d8bf8d1387..7a04b615fde 100644 --- a/sentry-compose/src/androidMain/kotlin/io/sentry/compose/SentryLayoutNodeHelper.kt +++ b/sentry-android-replay/src/main/java/io/sentry/android/replay/viewhierarchy/SentryLayoutNodeHelper.kt @@ -6,10 +6,9 @@ "EXPOSED_FUNCTION_RETURN_TYPE", ) -package io.sentry.compose +package io.sentry.android.replay.viewhierarchy import androidx.compose.ui.node.LayoutNode -import org.jetbrains.annotations.ApiStatus /** * Provides access to internal LayoutNode members that are subject to Kotlin name-mangling. @@ -19,8 +18,7 @@ import org.jetbrains.annotations.ApiStatus * Compose >= 1.10. This class detects the version on first use and delegates to the correct * accessor. */ -@ApiStatus.Internal -public object SentryLayoutNodeHelper { +internal object SentryLayoutNodeHelper { @Volatile private var compose110Helper: Compose110Helper? = null @Volatile private var useCompose110: Boolean? = null @@ -33,7 +31,7 @@ public object SentryLayoutNodeHelper { return helper } - public fun getChildren(node: LayoutNode): List { + fun getChildren(node: LayoutNode): List { return if (useCompose110 == false) { node.children } else { @@ -46,7 +44,7 @@ public object SentryLayoutNodeHelper { } } - public fun isTransparent(node: LayoutNode): Boolean { + fun isTransparent(node: LayoutNode): Boolean { return if (useCompose110 == false) { node.outerCoordinator.isTransparent() } else { diff --git a/sentry-compose/api/android/sentry-compose.api b/sentry-compose/api/android/sentry-compose.api index 55eca78df9d..3ae9af627b1 100644 --- a/sentry-compose/api/android/sentry-compose.api +++ b/sentry-compose/api/android/sentry-compose.api @@ -14,13 +14,6 @@ public final class io/sentry/compose/SentryComposeTracingKt { public static final fun SentryTraced (Ljava/lang/String;Landroidx/compose/ui/Modifier;ZLkotlin/jvm/functions/Function3;Landroidx/compose/runtime/Composer;II)V } -public final class io/sentry/compose/SentryLayoutNodeHelper { - public static final field $stable I - public static final field INSTANCE Lio/sentry/compose/SentryLayoutNodeHelper; - public final fun getChildren (Landroidx/compose/ui/node/LayoutNode;)Ljava/util/List; - public final fun isTransparent (Landroidx/compose/ui/node/LayoutNode;)Z -} - public final class io/sentry/compose/SentryModifier { public static final field $stable I public static final field INSTANCE Lio/sentry/compose/SentryModifier; diff --git a/sentry-compose/build.gradle.kts b/sentry-compose/build.gradle.kts index b5e6523a3b2..3385d0328e2 100644 --- a/sentry-compose/build.gradle.kts +++ b/sentry-compose/build.gradle.kts @@ -2,7 +2,6 @@ import io.gitlab.arturbosch.detekt.Detekt import org.jetbrains.dokka.gradle.DokkaTask import org.jetbrains.kotlin.gradle.dsl.JvmTarget import org.jetbrains.kotlin.gradle.dsl.KotlinVersion -import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { alias(libs.plugins.kotlin.multiplatform) @@ -119,80 +118,6 @@ android { } } -// Compile Compose110Helper.kt against Compose 1.10 where internal LayoutNode accessors -// are mangled with module name "ui" (e.g. getChildren$ui()) instead of "ui_release" -val compose110Classpath by - configurations.creating { - isCanBeConsumed = false - isCanBeResolved = true - attributes { - attribute(Attribute.of("artifactType", String::class.java), "android-classes-jar") - } - } - -val compose110KotlinCompiler by - configurations.creating { - isCanBeConsumed = false - isCanBeResolved = true - } - -dependencies { - //noinspection UseTomlInstead - compose110Classpath("androidx.compose.ui:ui-android:1.10.0") - //noinspection UseTomlInstead - compose110KotlinCompiler("org.jetbrains.kotlin:kotlin-compiler-embeddable:2.2.0") -} - -val compileCompose110 by - tasks.registering(JavaExec::class) { - val sourceDir = file("src/compose110/kotlin") - val outputDir = layout.buildDirectory.dir("classes/kotlin/compose110") - val compileClasspathFiles = compose110Classpath.incoming.files - - inputs.dir(sourceDir) - inputs.files(compileClasspathFiles) - outputs.dir(outputDir) - - classpath = compose110KotlinCompiler - mainClass.set("org.jetbrains.kotlin.cli.jvm.K2JVMCompiler") - - argumentProviders.add( - CommandLineArgumentProvider { - val cp = compileClasspathFiles.files.joinToString(File.pathSeparator) - outputDir.get().asFile.mkdirs() - listOf( - sourceDir.absolutePath, - "-classpath", - cp, - "-d", - outputDir.get().asFile.absolutePath, - "-jvm-target", - "1.8", - "-language-version", - "1.9", - "-opt-in=androidx.compose.ui.ExperimentalComposeUiApi", - "-Xsuppress-version-warnings", - "-no-stdlib", - ) - } - ) - } - -// Make compose110 output available to the Android Kotlin compilation -val compose110Output = files(compileCompose110.map { it.outputs.files }) - -tasks.withType().configureEach { - if (name == "compileReleaseKotlinAndroid" || name == "compileDebugKotlinAndroid") { - dependsOn(compileCompose110) - libraries.from(compose110Output) - } -} - -// Include compose110 classes in the AAR -android.libraryVariants.all { - registerPreJavacGeneratedBytecode(project.files(compileCompose110.map { it.outputs.files })) -} - tasks.withType().configureEach { // Target version of the generated JVM bytecode. It is used for type resolution. jvmTarget = JavaVersion.VERSION_1_8.toString()