fix(onboarding): warning, not block, when device has no lock#277
Conversation
Reverts the hard block from #213 sub-PR 6 for the "Create New Wallet" path. That gate refused creation outright when the device had neither biometric enrolled nor a credential set, because the V2 Keystore key needs *some* credential to bind to. In practice the wallet still works without a device lock — it just stays on kdfVersion=1 (non-auth-bound) until the user enrolls something, at which point the existing AuthScreen migration runner upgrades it automatically. User-reported issue: a fresh Pixel 7 Pro emulator with no lock set showed a snackbar reading "Set a screen lock... try again" with no path forward inside the app. That UX dead-end is the actual bug; the underlying security cost (no hardware-binding) is the user's choice to make. What changes - OnboardingViewModel.createNewWallet no longer returns early on !canCreateV2BoundKey. The wallet creates at kdfVersion=1 like any other wallet would have under the v1.6.x release. - OnboardingScreen intercepts the Create New Wallet tap when noDeviceCredential is true and surfaces a three-button dialog: Cancel — back to onboarding Open Settings — Intent.ACTION_SECURITY_SETTINGS Continue anyway — proceeds to the name dialog The "Continue anyway" path is the informed-consent surface. The "Open Settings" path is the friendly nudge for users who didn't realise they had no lock set. The snackbar wording remains as-is (vm_error_no_device_credential) because nothing fires it anymore on the create path. Kept the string so any future caller wanting the same gate can reuse it.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
📝 WalkthroughWalkthroughThe PR refactors device-credential handling for wallet creation. ChangesDevice Lock Warning Consent Flow
Estimated code review effort🎯 2 (Simple) | ⏱️ ~12 minutes Possibly related PRs
Suggested labels
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@android/app/src/main/java/com/rjnr/pocketnode/ui/screens/onboarding/OnboardingScreen.kt`:
- Around line 125-134: The branch in OnboardingScreen uses the stale
uiState.noDeviceCredential when handling the create flow (the onClick that sets
showNoDeviceLockWarning or showNameDialog); instead, refresh the
device-credential status immediately before branching (or on resume) by invoking
the ViewModel method that re-checks device credentials (e.g., call a
refresh/checkDeviceCredential function on the ViewModel) and use its up-to-date
result instead of the previously-cached uiState.noDeviceCredential; apply the
same change to the other handlers mentioned (lines around the second and third
occurrences that also read uiState.noDeviceCredential) so the gating always uses
the freshly-queried credential availability.
- Around line 228-231: The unguarded context.startActivity call in
OnboardingScreen (the Intent built with
android.provider.Settings.ACTION_SECURITY_SETTINGS) can throw
ActivityNotFoundException; fix by constructing the Intent first, then check that
the intent can be handled (e.g., call
context.packageManager.resolveActivity(intent, 0) != null) or wrap the
startActivity call in a runCatching and handle failure (log/show a fallback UI
or Toast) before invoking context.startActivity; update the code around the
context.startActivity(...) site in OnboardingScreen.kt to perform this guard and
provide a safe fallback path.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 1de6c8e6-4668-49b6-84ee-415313dc8214
📒 Files selected for processing (2)
android/app/src/main/java/com/rjnr/pocketnode/ui/screens/onboarding/OnboardingScreen.ktandroid/app/src/main/java/com/rjnr/pocketnode/ui/screens/onboarding/OnboardingViewModel.kt
| onClick = { | ||
| // Intercept the create flow when the device has no | ||
| // lock to give the user informed-consent before | ||
| // landing on the name dialog. They can still proceed. | ||
| if (uiState.noDeviceCredential) { | ||
| showNoDeviceLockWarning = true | ||
| } else { | ||
| showNameDialog = true | ||
| } | ||
| }, |
There was a problem hiding this comment.
Refresh noDeviceCredential before gating create flow.
Line 129 currently relies on uiState.noDeviceCredential, which is initialized once in the ViewModel. If the user enables a lock in Settings and returns, this value can stay stale and keep showing the warning incorrectly. Please re-check credential availability on resume or immediately before branching.
Also applies to: 199-216, 222-224
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@android/app/src/main/java/com/rjnr/pocketnode/ui/screens/onboarding/OnboardingScreen.kt`
around lines 125 - 134, The branch in OnboardingScreen uses the stale
uiState.noDeviceCredential when handling the create flow (the onClick that sets
showNoDeviceLockWarning or showNameDialog); instead, refresh the
device-credential status immediately before branching (or on resume) by invoking
the ViewModel method that re-checks device credentials (e.g., call a
refresh/checkDeviceCredential function on the ViewModel) and use its up-to-date
result instead of the previously-cached uiState.noDeviceCredential; apply the
same change to the other handlers mentioned (lines around the second and third
occurrences that also read uiState.noDeviceCredential) so the gating always uses
the freshly-queried credential availability.
| context.startActivity( | ||
| android.content.Intent(android.provider.Settings.ACTION_SECURITY_SETTINGS) | ||
| .addFlags(android.content.Intent.FLAG_ACTIVITY_NEW_TASK) | ||
| ) |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Inspect the current Settings launch call site.
fd -i OnboardingScreen.kt --exec sed -n '218,236p' {}
# Verify whether ACTION_SECURITY_SETTINGS launches are guarded in Kotlin sources.
rg -n -C2 --type=kt 'ACTION_SECURITY_SETTINGS|startActivity\s*\('Repository: RaheemJnr/pocket-node
Length of output: 1063
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE="android/app/src/main/java/com/rjnr/pocketnode/ui/screens/onboarding/OnboardingScreen.kt"
echo "---- Relevant snippet around the call site ----"
sed -n '200,260p' "$FILE" | nl -ba | sed -n '1,120p'
echo
echo "---- Search for ACTION_SECURITY_SETTINGS across repo ----"
rg -n --hidden --glob='*.kt' "android\.provider\.Settings\.ACTION_SECURITY_SETTINGS|ACTION_SECURITY_SETTINGS" .
echo
echo "---- Search for existing guards around Settings intent launches ----"
rg -n --hidden --glob='*.kt' "resolveActivity\(|packageManager\.resolveActivity|runCatching|FLAG_ACTIVITY_NEW_TASK|startActivity\(" "$FILE"Repository: RaheemJnr/pocket-node
Length of output: 174
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE="android/app/src/main/java/com/rjnr/pocketnode/ui/screens/onboarding/OnboardingScreen.kt"
echo "---- Relevant snippet around the call site (with line numbers) ----"
cat -n "$FILE" | sed -n '210,250p'
echo
echo "---- Search for ACTION_SECURITY_SETTINGS across repo (KT only) ----"
rg -n --glob='*.kt' "android\.provider\.Settings\.ACTION_SECURITY_SETTINGS|ACTION_SECURITY_SETTINGS" .
echo
echo "---- Search for possible guards for Settings launches in this file ----"
rg -n --glob='*.kt' "resolveActivity\(|packageManager\.resolveActivity|runCatching\(" "$FILE"
echo
echo "---- Search for startActivity calls around this file (context) ----"
rg -n --glob='*.kt' "startActivity\s*\(" "$FILE"Repository: RaheemJnr/pocket-node
Length of output: 2373
Guard the ACTION_SECURITY_SETTINGS launch to avoid ActivityNotFoundException.
In android/app/src/main/java/com/rjnr/pocketnode/ui/screens/onboarding/OnboardingScreen.kt (lines 228–231), context.startActivity(Intent(android.provider.Settings.ACTION_SECURITY_SETTINGS)...) is unguarded (no resolveActivity(...)/runCatching). Add a check (e.g., packageManager.resolveActivity(intent, 0) != null) or wrap the launch to prevent a crash on devices/ROMs where the intent isn’t handled.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@android/app/src/main/java/com/rjnr/pocketnode/ui/screens/onboarding/OnboardingScreen.kt`
around lines 228 - 231, The unguarded context.startActivity call in
OnboardingScreen (the Intent built with
android.provider.Settings.ACTION_SECURITY_SETTINGS) can throw
ActivityNotFoundException; fix by constructing the Intent first, then check that
the intent can be handled (e.g., call
context.packageManager.resolveActivity(intent, 0) != null) or wrap the
startActivity call in a runCatching and handle failure (log/show a fallback UI
or Toast) before invoking context.startActivity; update the code around the
context.startActivity(...) site in OnboardingScreen.kt to perform this guard and
provide a safe fallback path.
Summary
User-reported on the smoke build: a fresh Pixel 7 Pro emulator with no device lock showed a snackbar reading "Set a screen lock... try again" when tapping Create New Wallet — a UX dead-end with no path forward inside the app.
Reverts the hard block from #213 sub-PR 6. The wallet still works without a device lock; it just stays on kdfVersion=1 until the user enrolls a credential, at which point the existing AuthScreen migration runner upgrades it automatically.
What changes
`OnboardingViewModel.createNewWallet` no longer returns early on `!canCreateV2BoundKey`. Wallets create at `kdfVersion=1` like any other wallet under v1.6.x would have.
`OnboardingScreen` intercepts the Create New Wallet tap when `noDeviceCredential` is true and surfaces a three-button dialog:
"Continue anyway" is the informed-consent surface — the user knows the trade-off and chooses to proceed. "Open Settings" is the friendly nudge for users who didn't realise they had no lock set.
Test plan
Refs #213
Summary by CodeRabbit