Skip to content

fix(navigation): Defer onNewIntent navCtrl call until Compose attaches#587

Merged
d4rken merged 1 commit into
mainfrom
fix-navcontroller-onnewintent-race
May 4, 2026
Merged

fix(navigation): Defer onNewIntent navCtrl call until Compose attaches#587
d4rken merged 1 commit into
mainfrom
fix-navcontroller-onnewintent-race

Conversation

@d4rken

@d4rken d4rken commented May 4, 2026

Copy link
Copy Markdown
Member

Summary

Latent twin of d4rken-org/octi#284 — same NavigationController design (@Singleton + nullable _backStack + strict error("...not initialized")), same Compose setContent { } registration pattern, same race surface.

MainActivity.onNewIntent calls consumeUpgradeExtra(intent), which in turn calls navCtrl.goTo(Nav.Main.Upgrade). If onNewIntent fires before the setContent { } lambda has run (e.g. during activity recreation from a config change, or singleTask warm intent delivery on a slow device), _backStack is still null and the goTo throws IllegalStateException.

This PR mirrors the fix shipped to Octi: defer the navigation step until a Compose-side collector picks it up.

Changes

  • New private MainActivity.warmIntents: SingleEventFlow<Intent> field
  • onNewIntent now just calls setIntent(intent) and warmIntents.tryEmit(intent) — no navCtrl interaction
  • The existing LaunchedEffect(Unit) inside setContent extends to also warmIntents.collect { newIntent -> consumeUpgradeExtra(newIntent) } after handling the cold-start intent
  • NavigationController is unchanged — strict contract preserved

The race window is tight (it requires onNewIntent to fire between setContent returning and the Compose lambda running), so this is a latent crash with no Play Store report yet — but the same code path crashed Octi on Android 16 Beta. Fixing here pre-emptively.

Test plan

  • ./gradlew :app:testFossDebugUnitTest passes
  • ./gradlew :app:assembleFossDebug compiles cleanly
  • Manual: launch the upgrade flow via the existing pathway that fires EXTRA_NAVIGATE_TO_UPGRADE (e.g. via EXTRA_UPGRADE_FOR_RESULT flow or the link that triggers it) — confirm Upgrade screen still opens

Move consumeUpgradeExtra() out of MainActivity.onNewIntent and into a Compose-level LaunchedEffect that collects a new MainActivity.warmIntents SingleEventFlow.

onNewIntent could fire before the setContent{} lambda registered the back stack with NavigationController, leaving navCtrl.goTo(Nav.Main.Upgrade) to throw IllegalStateException("NavigationController not initialized"). Same race shipped a fix for in octi (d4rken-org/octi#284).
@d4rken d4rken added the bug Something isn't working label May 4, 2026
@d4rken d4rken merged commit ebfb034 into main May 4, 2026
11 checks passed
@d4rken d4rken deleted the fix-navcontroller-onnewintent-race branch May 4, 2026 19:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant