Skip to content

Commit 6e8b449

Browse files
authored
feat(android-nav3): Android Nav3 integration (JAVA-274) (#6128)
Initial Nav3 support (internal and experimental) Commit introduces a new sentry-android-navigation3 module and SentryNavEffect, our primary abstraction for phase 1 Nav3 support. Phase 1 aims at achieving parity with our existing Nav2 integration. That means generating nav transactions, breadcrumbs, and screen names for each destination as the user navigates through the app. In line with Nav2, SentryNavEffect assumes the top of the nav back stack represents the current screen and the entirety of its contents. Phase 2 will introduce support for [Scenes](https://developer.android.com/guide/navigation/navigation-3/scenes) and multipane layouts; phase 3 will support multiple back stacks (See [#5649](#5649) and [#5650](#5650), respectively.) In addition to what Nav2 offers, SentryNavEffect also records up to N frames of the host app's back stack in current scope context. (N is 10 by default; it can be adjusted via SentryNavOptions, along with other nav3 configs.) For more details, see the KDocs to SentryNavEffect, SentryNavOptions, RouteNameExtractor, and RouteArgumentsExtractor. SentryNavEffect is experimental and (for now) internal. It will be made it public in a later release commit.
1 parent 91fbff1 commit 6e8b449

22 files changed

Lines changed: 3586 additions & 1 deletion

File tree

‎.github/ISSUE_TEMPLATE/bug_report_android.yml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ body:
1212
- sentry-android-ndk
1313
- sentry-android-timber
1414
- sentry-android-fragment
15+
- sentry-android-navigation3
1516
- sentry-android-sqlite
1617
- sentry-apollo
1718
- sentry-apollo-3

‎AGENTS.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ The repository is organized into multiple modules:
9898
- **Logging**: `sentry-logback`, `sentry-log4j2`, `sentry-jul`, `sentry-android-timber`
9999
- **Web**: `sentry-servlet*`, `sentry-okhttp`, `sentry-openfeign`, `sentry-apache-http-client-5`
100100
- **GraphQL**: `sentry-graphql*`, `sentry-apollo*`
101-
- **Android UI**: `sentry-android-fragment`, `sentry-android-navigation`, `sentry-compose`
101+
- **Android UI**: `sentry-android-fragment`, `sentry-android-navigation`, `sentry-android-navigation3`, `sentry-compose`
102102
- **Session Replay**: `sentry-android-replay`
103103
- **Database**: `sentry-jdbc`, `sentry-android-sqlite`, `sentry-jcache`
104104
- **Reactive**: `sentry-reactor`, `sentry-ktor-client`

‎buildSrc/src/main/java/Config.kt‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ object Config {
9898
"sentry-android-ndk",
9999
"sentry-android-fragment",
100100
"sentry-android-navigation",
101+
"sentry-android-navigation3",
101102
"sentry-android-timber",
102103
"sentry-compose-android",
103104
"sentry-android-sqlite",

‎gradle/libs.versions.toml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ androidx-activity-compose = { module = "androidx.activity:activity-compose", ver
9191
androidx-compose-foundation = { module = "androidx.compose.foundation:foundation", version.ref = "androidxCompose" }
9292
androidx-compose-foundation-layout = { module = "androidx.compose.foundation:foundation-layout", version.ref = "androidxCompose" }
9393
androidx-compose-material3 = { module = "androidx.compose.material3:material3", version = "1.4.0" }
94+
androidx-compose-runtime = { module = "androidx.compose.runtime:runtime", version.ref = "androidxCompose" }
9495
androidx-compose-material-icons-core = { module = "androidx.compose.material:material-icons-core", version="1.7.8" }
9596
androidx-compose-material-icons-extended = { module = "androidx.compose.material:material-icons-extended", version="1.7.8" }
9697
androidx-compose-ui = { module = "androidx.compose.ui:ui", version.ref = "androidxCompose" }
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
public final class io/sentry/compose/navigation3/BuildConfig {
2+
public static final field BUILD_TYPE Ljava/lang/String;
3+
public static final field DEBUG Z
4+
public static final field LIBRARY_PACKAGE_NAME Ljava/lang/String;
5+
public static final field VERSION_NAME Ljava/lang/String;
6+
public fun <init> ()V
7+
}
8+
9+
public abstract interface class io/sentry/compose/navigation3/RouteArgumentsExtractor {
10+
public abstract fun extract (Ljava/lang/Object;)Ljava/util/Map;
11+
}
12+
13+
public abstract interface class io/sentry/compose/navigation3/RouteNameExtractor {
14+
public abstract fun extract (Ljava/lang/Object;)Ljava/lang/String;
15+
}
16+
17+
public final class io/sentry/compose/navigation3/SentryNavEffectKt {
18+
public static final fun SentryNavEffect (Ljava/util/List;Lio/sentry/compose/navigation3/RouteNameExtractor;Lio/sentry/compose/navigation3/RouteArgumentsExtractor;Lio/sentry/compose/navigation3/SentryNavOptions;Landroidx/compose/runtime/Composer;II)V
19+
}
20+
21+
public final class io/sentry/compose/navigation3/SentryNavOptions {
22+
public static final field $stable I
23+
public synthetic fun <init> (ZZZILkotlin/jvm/internal/DefaultConstructorMarker;)V
24+
public fun equals (Ljava/lang/Object;)Z
25+
public final fun getCaptureBackStack ()Z
26+
public final fun getEnableNavigationBreadcrumbs ()Z
27+
public final fun getEnableNavigationTransactions ()Z
28+
public final fun getMaxCapturedBackStackEntries ()I
29+
public fun hashCode ()I
30+
public fun toString ()Ljava/lang/String;
31+
}
32+
33+
public final class io/sentry/compose/navigation3/SentryNavOptions$Builder {
34+
public static final field $stable I
35+
public fun <init> ()V
36+
public final fun build ()Lio/sentry/compose/navigation3/SentryNavOptions;
37+
public final fun getCaptureBackStack ()Z
38+
public final fun getEnableNavigationBreadcrumbs ()Z
39+
public final fun getEnableNavigationTransactions ()Z
40+
public final fun getMaxCapturedBackStackEntries ()I
41+
public final fun setCaptureBackStack (Z)V
42+
public final fun setEnableNavigationBreadcrumbs (Z)V
43+
public final fun setEnableNavigationTransactions (Z)V
44+
public final fun setMaxCapturedBackStackEntries (I)V
45+
}
46+
47+
public final class io/sentry/compose/navigation3/SentryNavOptionsKt {
48+
public static final fun SentryNavOptions (Lkotlin/jvm/functions/Function1;)Lio/sentry/compose/navigation3/SentryNavOptions;
49+
public static synthetic fun SentryNavOptions$default (Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lio/sentry/compose/navigation3/SentryNavOptions;
50+
}
51+
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import io.gitlab.arturbosch.detekt.Detekt
2+
import org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_1_8
3+
import org.jetbrains.kotlin.gradle.dsl.KotlinVersion
4+
5+
plugins {
6+
id("com.android.library")
7+
alias(libs.plugins.kotlin.android)
8+
alias(libs.plugins.kotlin.compose)
9+
alias(libs.plugins.gradle.versions)
10+
alias(libs.plugins.detekt)
11+
}
12+
13+
android {
14+
compileSdk = libs.versions.compileSdk.get().toInt()
15+
namespace = "io.sentry.compose.navigation3"
16+
17+
defaultConfig {
18+
minSdk = libs.versions.minSdk.get().toInt()
19+
20+
buildConfigField("String", "VERSION_NAME", "\"${project.version}\"")
21+
}
22+
23+
buildTypes {
24+
getByName("release") { consumerProguardFiles("proguard-rules.pro") }
25+
}
26+
27+
// AGP 9 only generates unit tests for the testBuildType. The debug variant is
28+
// disabled, so unit tests must target release to run at all.
29+
testBuildType = "release"
30+
31+
kotlin {
32+
compilerOptions.jvmTarget = JVM_1_8
33+
compilerOptions.languageVersion = KotlinVersion.KOTLIN_1_9
34+
compilerOptions.apiVersion = KotlinVersion.KOTLIN_1_9
35+
}
36+
37+
testOptions {
38+
unitTests.isReturnDefaultValues = true
39+
}
40+
41+
lint {
42+
warningsAsErrors = true
43+
checkDependencies = true
44+
45+
// We run a full lint analysis as build part in CI, so skip vital checks for assemble tasks.
46+
checkReleaseBuilds = false
47+
}
48+
49+
buildFeatures {
50+
buildConfig = true
51+
compose = true
52+
}
53+
54+
androidComponents.beforeVariants {
55+
it.enable = !Config.Android.shouldSkipDebugVariant(it.buildType)
56+
}
57+
}
58+
59+
kotlin { explicitApi() }
60+
61+
dependencies {
62+
implementation(projects.sentry)
63+
64+
compileOnly(libs.androidx.compose.runtime)
65+
66+
testImplementation(libs.androidx.compose.ui.test.junit4)
67+
testImplementation(libs.androidx.test.core)
68+
testImplementation(libs.androidx.test.ext.junit)
69+
testImplementation(libs.google.truth)
70+
testImplementation(libs.mockito.inline)
71+
testImplementation(libs.mockito.kotlin)
72+
testImplementation(libs.roboelectric)
73+
}
74+
75+
tasks.withType<Detekt>().configureEach {
76+
// Target version of the generated JVM bytecode. It is used for type resolution.
77+
jvmTarget = JavaVersion.VERSION_1_8.toString()
78+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
##---------------Begin: proguard configuration for Compose ----------
2+
3+
# To ensure that stack traces is unambiguous
4+
# https://developer.android.com/studio/build/shrink-code#decode-stack-trace
5+
-keepattributes LineNumberTable,SourceFile
6+
7+
##---------------End: proguard configuration for Compose ----------
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package io.sentry.compose.navigation3
2+
3+
/**
4+
* A key for distinguishing back stacks over time.
5+
*
6+
* Lets `*Effect`s restart when either the identity of a stack entry changes or the stack's entries
7+
* are reordered.
8+
*/
9+
internal class BackStackKey<T : Any>(private val backStack: List<T>) {
10+
11+
override fun equals(other: Any?): Boolean {
12+
// Use of identity rather than structural equality frees us from entries' equals() and
13+
// hashCode() implementations, which are provided by the host app and may be incomplete,
14+
// expensive, or incorrect for our purposes.
15+
if (this === other) {
16+
return true
17+
}
18+
if (other !is BackStackKey<*>) {
19+
return false
20+
}
21+
if (backStack.size != other.backStack.size) {
22+
return false
23+
}
24+
25+
return backStack.indices.all { index -> backStack[index] === other.backStack[index] }
26+
}
27+
28+
override fun hashCode(): Int {
29+
var result = backStack.size
30+
for (entry in backStack) {
31+
result = 31 * result + System.identityHashCode(entry)
32+
}
33+
return result
34+
}
35+
}

0 commit comments

Comments
 (0)