Skip to content

fix(onboarding): warning, not block, when device has no lock#277

Merged
RaheemJnr merged 3 commits into
mainfrom
fix/no-device-lock-warning-only
May 22, 2026
Merged

fix(onboarding): warning, not block, when device has no lock#277
RaheemJnr merged 3 commits into
mainfrom
fix/no-device-lock-warning-only

Conversation

@RaheemJnr
Copy link
Copy Markdown
Owner

@RaheemJnr RaheemJnr commented May 22, 2026

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:

    Action Result
    Cancel Back to onboarding
    Open Settings `Intent.ACTION_SECURITY_SETTINGS`
    Continue anyway Proceeds to the name 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

  • `./gradlew :app:compileDebugKotlin -x cargoBuild` BUILD SUCCESSFUL
  • APK rebuilt for smoke testing
  • Manual: fresh emulator without device lock → Create New Wallet → warning dialog → Continue anyway → name dialog → wallet creates
  • Manual: same emulator → Open Settings → Android Security settings opens
  • Manual: enroll a PIN/biometric → close app → reopen → Create New Wallet → no warning, V2 path takes over

Refs #213

Summary by CodeRabbit

  • New Features
    • Added a security warning dialog during wallet creation when device lock is not enabled. Users can now choose to continue, open device security settings, or cancel the operation.

Review Change Stack

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.
@vercel
Copy link
Copy Markdown

vercel Bot commented May 22, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
pocket-node Ready Ready Preview, Comment May 22, 2026 4:18pm

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 22, 2026

📝 Walkthrough

Walkthrough

The PR refactors device-credential handling for wallet creation. OnboardingViewModel.createNewWallet() removes the early-return validation that previously blocked creation when device credentials were missing. OnboardingScreen adds a UI warning state and AlertDialog to conditionally inform users and offer options to proceed, open Security settings, or cancel wallet creation.

Changes

Device Lock Warning Consent Flow

Layer / File(s) Summary
ViewModel credential validation removal
android/app/src/main/java/com/rjnr/pocketnode/ui/screens/onboarding/OnboardingViewModel.kt
createNewWallet no longer performs early-return validation when device credentials are missing (canCreateV2BoundKey() is false). Wallet creation proceeds after name trimming, deferring credential consent handling to the UI layer.
Screen warning state and dialog
android/app/src/main/java/com/rjnr/pocketnode/ui/screens/onboarding/OnboardingScreen.kt
Added showNoDeviceLockWarning state to track warning visibility. Updated "Create New Wallet" click handler to conditionally show an AlertDialog when uiState.noDeviceCredential is true. Dialog offers three actions: continue to wallet-name dialog, launch Android Security settings, or dismiss the warning.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Possibly related PRs

  • RaheemJnr/pocket-node#274: Both PRs modify the onboarding "Create New Wallet" flow by changing OnboardingScreen.kt and OnboardingViewModel.kt behavior (main PR adds device-lock gating/warning; retrieved PR adds the wallet-name prompt and parameterized createNewWallet).

Suggested labels

codex

Poem

🐰 A wallet's born, but first a check—
Does the device have a lock, by heck?
If not, a warning pops right up,
"Go fix your settings, fill this cup!"
The ViewModel steps back with grace,
The UI takes the warning's place. 🔐

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix(onboarding): warning, not block, when device has no lock' directly and clearly summarizes the main change: shifting from blocking wallet creation to showing a warning when device lacks a lock.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/no-device-lock-warning-only

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between 3c4edd7 and 54e0ae6.

📒 Files selected for processing (2)
  • android/app/src/main/java/com/rjnr/pocketnode/ui/screens/onboarding/OnboardingScreen.kt
  • android/app/src/main/java/com/rjnr/pocketnode/ui/screens/onboarding/OnboardingViewModel.kt

Comment on lines +125 to +134
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
}
},
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

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.

Comment on lines +228 to +231
context.startActivity(
android.content.Intent(android.provider.Settings.ACTION_SECURITY_SETTINGS)
.addFlags(android.content.Intent.FLAG_ACTIVITY_NEW_TASK)
)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 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.

@RaheemJnr RaheemJnr merged commit a1b1ab7 into main May 22, 2026
5 of 6 checks passed
@RaheemJnr RaheemJnr deleted the fix/no-device-lock-warning-only branch May 22, 2026 16:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant