Implements the onboarding epic (#26–#31) as one coherent feature.
What
#26 — First-run nav scaffold + welcome.AppViewModel gates the NavHost start destination on the stored account count (no accounts → onboarding, ≥1 → MAILBOX); the UI renders nothing until the count is known, so a cold start never flashes the wrong screen. Onboarding is a nested nav graph with a graph-scoped OnboardingViewModel that tracks the first account added this session. NoAccountState is removed in favour of a shared WelcomeContent used by both onboarding and the mailbox's runtime empty state.
#28 — Provider registry.MailProvider presets for Gmail/Yahoo/iCloud (IMAP+SMTP host/port/MailSecurity, app-password help URL, createAccount factory) mirroring Account.outlook, biased to STARTTLS where the vendor documents it (Gmail/iCloud SMTP 587), implicit TLS otherwise (all IMAP 993, Yahoo SMTP 465); never NONE. Host/port/security unit-tested for all three.
#27 — Vendor picker.AccountPickerScreen replaces the old two-button setup screen as the single entry point used by onboarding and the Settings/mailbox "Add account". Routes Outlook/Hotmail → existing MS OAuth (inline), Gmail/Yahoo/iCloud → app-password screen with the matching preset, Other → existing manual setup.
#29 — App-password guided screen. One reusable AppPasswordSetupScreen parameterized by provider key: explains what an app password is, warns to store it securely, links to the provider's app-password page (guarded against a missing browser), and collects only email + app password (servers come from the preset, shown read-only under "Server settings"). Verifies + persists via the existing addImapAccount. ViewModel unit tests cover valid input, blank input, connection failure, and unknown provider.
#30 — "Add another?" + first-account landing. After every onboarding add (OAuth, app-password, manual), an "Add another account?" prompt: Yes → back to the picker; No → open the first account added this session, filtered to its inbox (new optional account arg on the mailbox route; MailboxViewModel seeds the filter from it). Only inside onboarding — "Add account" from Settings/mailbox pops back to where the user was.
#31 — E2E + strings. Instrumented OnboardingFlowTest drives welcome → picker → app-password add → "add another?" → first account's inbox. Managed-device list and CI E2E matrix are already in lockstep (29–36). All new files carry the SPDX header; new copy is in strings.xml.
Testing
Fast CI gate green locally: assembleDebug + testDebugUnitTest (98 tests) + lintDebug + ktlintCheck + detekt, plus compileDebugAndroidTestKotlin.
Emulator E2E left to CI.
Notes
README reconciliation is intentionally deferred (owned by a separate unit), per the epic.
The onboarding E2E is backed by an in-memory FakeAccountRepository (consistent with the existing instrumented-test infra, which has no Hilt/GreenMail wiring); GreenMail-backed connection behaviour is covered by the repository unit tests.
Implements the onboarding epic (#26–#31) as one coherent feature.
## What
- **#26 — First-run nav scaffold + welcome.** `AppViewModel` gates the NavHost start destination on the stored account count (no accounts → onboarding, ≥1 → `MAILBOX`); the UI renders nothing until the count is known, so a cold start never flashes the wrong screen. Onboarding is a nested nav graph with a graph-scoped `OnboardingViewModel` that tracks the first account added this session. `NoAccountState` is removed in favour of a shared `WelcomeContent` used by both onboarding and the mailbox's runtime empty state.
- **#28 — Provider registry.** `MailProvider` presets for Gmail/Yahoo/iCloud (IMAP+SMTP host/port/`MailSecurity`, app-password help URL, `createAccount` factory) mirroring `Account.outlook`, biased to STARTTLS where the vendor documents it (Gmail/iCloud SMTP 587), implicit TLS otherwise (all IMAP 993, Yahoo SMTP 465); never `NONE`. Host/port/security unit-tested for all three.
- **#27 — Vendor picker.** `AccountPickerScreen` replaces the old two-button setup screen as the **single** entry point used by onboarding and the Settings/mailbox "Add account". Routes Outlook/Hotmail → existing MS OAuth (inline), Gmail/Yahoo/iCloud → app-password screen with the matching preset, Other → existing manual setup.
- **#29 — App-password guided screen.** One reusable `AppPasswordSetupScreen` parameterized by provider key: explains what an app password is, warns to store it securely, links to the provider's app-password page (guarded against a missing browser), and collects only email + app password (servers come from the preset, shown read-only under "Server settings"). Verifies + persists via the existing `addImapAccount`. ViewModel unit tests cover valid input, blank input, connection failure, and unknown provider.
- **#30 — "Add another?" + first-account landing.** After every onboarding add (OAuth, app-password, manual), an "Add another account?" prompt: Yes → back to the picker; No → open the **first** account added this session, filtered to its inbox (new optional `account` arg on the mailbox route; `MailboxViewModel` seeds the filter from it). Only inside onboarding — "Add account" from Settings/mailbox pops back to where the user was.
- **#31 — E2E + strings.** Instrumented `OnboardingFlowTest` drives welcome → picker → app-password add → "add another?" → first account's inbox. Managed-device list and CI E2E matrix are already in lockstep (`29–36`). All new files carry the SPDX header; new copy is in `strings.xml`.
## Testing
- Fast CI gate green locally: `assembleDebug` + `testDebugUnitTest` (98 tests) + `lintDebug` + `ktlintCheck` + `detekt`, plus `compileDebugAndroidTestKotlin`.
- Emulator E2E left to CI.
## Notes
- README reconciliation is intentionally **deferred** (owned by a separate unit), per the epic.
- The onboarding E2E is backed by an in-memory `FakeAccountRepository` (consistent with the existing instrumented-test infra, which has no Hilt/GreenMail wiring); GreenMail-backed connection behaviour is covered by the repository unit tests.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Implements the onboarding epic (#26–#31) as one coherent feature.
What
AppViewModelgates the NavHost start destination on the stored account count (no accounts → onboarding, ≥1 →MAILBOX); the UI renders nothing until the count is known, so a cold start never flashes the wrong screen. Onboarding is a nested nav graph with a graph-scopedOnboardingViewModelthat tracks the first account added this session.NoAccountStateis removed in favour of a sharedWelcomeContentused by both onboarding and the mailbox's runtime empty state.MailProviderpresets for Gmail/Yahoo/iCloud (IMAP+SMTP host/port/MailSecurity, app-password help URL,createAccountfactory) mirroringAccount.outlook, biased to STARTTLS where the vendor documents it (Gmail/iCloud SMTP 587), implicit TLS otherwise (all IMAP 993, Yahoo SMTP 465); neverNONE. Host/port/security unit-tested for all three.AccountPickerScreenreplaces the old two-button setup screen as the single entry point used by onboarding and the Settings/mailbox "Add account". Routes Outlook/Hotmail → existing MS OAuth (inline), Gmail/Yahoo/iCloud → app-password screen with the matching preset, Other → existing manual setup.AppPasswordSetupScreenparameterized by provider key: explains what an app password is, warns to store it securely, links to the provider's app-password page (guarded against a missing browser), and collects only email + app password (servers come from the preset, shown read-only under "Server settings"). Verifies + persists via the existingaddImapAccount. ViewModel unit tests cover valid input, blank input, connection failure, and unknown provider.accountarg on the mailbox route;MailboxViewModelseeds the filter from it). Only inside onboarding — "Add account" from Settings/mailbox pops back to where the user was.OnboardingFlowTestdrives welcome → picker → app-password add → "add another?" → first account's inbox. Managed-device list and CI E2E matrix are already in lockstep (29–36). All new files carry the SPDX header; new copy is instrings.xml.Testing
assembleDebug+testDebugUnitTest(98 tests) +lintDebug+ktlintCheck+detekt, pluscompileDebugAndroidTestKotlin.Notes
FakeAccountRepository(consistent with the existing instrumented-test infra, which has no Hilt/GreenMail wiring); GreenMail-backed connection behaviour is covered by the repository unit tests.🤖 Generated with Claude Code