Closes #229.
The Recents relaunch
if (savedInstanceState == null) handleIncomingShortcut(intent)An activity relaunched from Recents after its task finished carries the intent the app was first opened with, and savedInstanceState is null, so the shortcut the person chose once was dispatched to the page again — every time they reopened the task.
The link sitting in the same intent was already skipped for exactly this reason, by FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY. The flag now guards both, which is the right shape for it: the person picked the task out of a list, not the link or the shortcut inside it, so neither is news the second time.
val launchedFromHistory = intent.flags and Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY != 0
if (savedInstanceState == null) {
if (!launchedFromHistory) {
intent.data?.toString()?.let(craftBridge::receiveDeepLink)
handleIncomingShortcut(intent)
}
} else {
craftBridge.restoreDeepLinks(savedInstanceState)
}onNewIntent still passes both through untouched — a genuinely new intent is a choice the person just made.
The other half was already fixed
The issue also asks for the launch URL to be saved in onSaveInstanceState and restored from there rather than from getIntent(), which onNewIntent replaces with the latest warm link.
That landed with #232. saveDeepLinks writes STATE_LAUNCH_URL alongside the two flags, restoreDeepLinks reads them back, and onCreate's else branch calls it instead of touching the intent. The restoreInitialURL the issue quotes no longer exists.
So there was nothing to change for it — but nothing pinning it either, and it is one line away from coming back. The test asserts it now.
Tests that fail without the fix
Two mutations:
| mutation | result |
|---|---|
| the shortcut moved back outside the history guard | fails |
a recreate reads getIntent() again instead of the bundle | fails, 2 tests |
The first one caught a weakness in my own test on the way: slicing onCreate from if (!launchedFromHistory) { to the end still contained a shortcut call that had moved out of the block, so the mutation passed. The slice is now bounded by the block's closing brace, and the call is required to appear exactly once in onCreate.
Verification
bun run typecheck, bun run verify:packages, Pickier (0 errors), and the android package tests (103). The Kotlin compile runs in CI, as always for these templates.