Skip to content

Commit 37dac13

Browse files
0xadam-browncodex
andcommitted
chore(android): Add Nav2 sample app
Adds a Nav2 sample app for validating Sentry's fragment and Compose navigation integrations under different tracing setups. Developers can configure active transactions (nav vs ui.load vs user interaction), navigation data (breadcrumbs and screen names), and work performed at each nav destination (HTTP requets, manual spans, or nothing). They can also view transaction/span generation in real time via the Transaction History Bottom Sheet. Co-Authored-By: OpenAI GPT-5.4 <noreply@openai.com>
1 parent 99b7d9d commit 37dac13

21 files changed

Lines changed: 4059 additions & 0 deletions

‎gradle/libs.versions.toml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ androidx-lifecycle-common-java8 = { module = "androidx.lifecycle:lifecycle-commo
103103
androidx-lifecycle-process = { module = "androidx.lifecycle:lifecycle-process", version.ref = "androidxLifecycle" }
104104
androidx-navigation-runtime = { module = "androidx.navigation:navigation-runtime", version.ref = "androidxNavigation" }
105105
androidx-navigation-compose = { module = "androidx.navigation:navigation-compose", version.ref = "androidxNavigation" }
106+
androidx-navigation-fragment = { module = "androidx.navigation:navigation-fragment-ktx", version.ref = "androidxNavigation" }
106107
androidx-room-compiler = { module = "androidx.room:room-compiler", version.ref = "room2" }
107108
androidx-room-ktx = { module = "androidx.room:room-ktx", version.ref = "room2" }
108109
androidx-room-runtime = { module = "androidx.room:room-runtime", version.ref = "room2" }

‎sentry-samples/sentry-samples-android/build.gradle.kts‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,7 @@ dependencies {
185185

186186
implementation(projects.sentryAndroid)
187187
implementation(projects.sentryAndroidFragment)
188+
implementation(projects.sentryAndroidNavigation)
188189
implementation(projects.sentryAndroidSqlite)
189190
implementation(projects.sentryAndroidTimber)
190191
implementation(projects.sentryCompose)
@@ -209,6 +210,7 @@ dependencies {
209210
implementation(libs.androidx.compose.material.icons.core)
210211
implementation(libs.androidx.compose.material.icons.extended)
211212
implementation(libs.androidx.navigation.compose)
213+
implementation(libs.androidx.navigation.fragment)
212214
implementation(libs.androidx.recyclerview)
213215
implementation(libs.androidx.browser)
214216
implementation(libs.androidx.room3.runtime)

‎sentry-samples/sentry-samples-android/src/main/AndroidManifest.xml‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,16 @@
101101
android:name=".compose.ComposeActivity"
102102
android:exported="false" />
103103

104+
<activity
105+
android:name=".navigation.Nav2SetupActivity"
106+
android:exported="false"
107+
android:theme="@style/AppTheme.Main" />
108+
109+
<activity
110+
android:name=".navigation.Nav2Activity"
111+
android:exported="false"
112+
android:theme="@style/AppTheme.Main" />
113+
104114
<activity
105115
android:name=".FrameDataForSpansActivity"
106116
android:exported="false" />

‎sentry-samples/sentry-samples-android/src/main/java/io/sentry/samples/android/MainActivity.kt‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -893,6 +893,20 @@ fun IntegrationsScreen() {
893893
}
894894
}
895895
}
896+
item {
897+
SentryTraced("open_nav2_activity") {
898+
OutlinedButton(
899+
onClick = {
900+
activity.startActivity(
901+
Intent(activity, io.sentry.samples.android.navigation.Nav2SetupActivity::class.java)
902+
)
903+
},
904+
modifier = Modifier,
905+
) {
906+
Text("Open Nav2Activity", maxLines = 2, overflow = TextOverflow.Ellipsis)
907+
}
908+
}
909+
}
896910
item {
897911
SentryTraced("open_sample_fragment") {
898912
OutlinedButton(
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package io.sentry.samples.android
2+
3+
import io.sentry.SentryOptions
4+
import io.sentry.protocol.SentryTransaction
5+
import java.util.concurrent.CopyOnWriteArrayList
6+
7+
/**
8+
* Owns the single global beforeSendTransaction hook used by sample screens that inspect finished
9+
* transactions.
10+
*
11+
* Multiple sample activities can overlap briefly during configuration changes or relaunches. If
12+
* each screen installs and restores its own callback, a later uninstall can resurrect a stale
13+
* wrapper callback that still retains dead state and drops the original callback chain.
14+
*
15+
* Keep one stable callback installed and let sample screens register lightweight listeners against
16+
* it instead.
17+
*/
18+
internal object SampleBeforeSendTransactionHook {
19+
20+
private val listeners = CopyOnWriteArrayList<(SentryTransaction, String?) -> Unit>()
21+
22+
@Volatile private var installedCallback: SentryOptions.BeforeSendTransactionCallback? = null
23+
@Volatile private var previousCallback: SentryOptions.BeforeSendTransactionCallback? = null
24+
25+
fun installIfNeeded(options: SentryOptions) {
26+
if (installedCallback != null) {
27+
return
28+
}
29+
30+
synchronized(this) {
31+
if (installedCallback != null) {
32+
return
33+
}
34+
35+
previousCallback = options.beforeSendTransaction
36+
val callback = SentryOptions.BeforeSendTransactionCallback { transaction, hint ->
37+
val previous = previousCallback
38+
39+
val processedTransaction =
40+
if (previous == null) {
41+
transaction
42+
} else {
43+
previous.execute(transaction, hint)
44+
}
45+
46+
processedTransaction?.let { processed ->
47+
listeners.forEach { listener ->
48+
try {
49+
listener(processed, options.dsn)
50+
} catch (e: RuntimeException) {
51+
options.logger.log(
52+
io.sentry.SentryLevel.ERROR,
53+
"Sample transaction listener failed.",
54+
e,
55+
)
56+
}
57+
}
58+
}
59+
60+
processedTransaction
61+
}
62+
63+
installedCallback = callback
64+
options.beforeSendTransaction = callback
65+
}
66+
}
67+
68+
fun addListener(listener: (SentryTransaction, String?) -> Unit) {
69+
listeners.addIfAbsent(listener)
70+
}
71+
72+
fun removeListener(listener: (SentryTransaction, String?) -> Unit) {
73+
listeners.remove(listener)
74+
}
75+
}

0 commit comments

Comments
 (0)