Skip to content

feat(settings): module-owned settings screen with change password, biometrics, and autofill#60

Merged
OffRange merged 27 commits into
v2from
feat/settings
Jun 10, 2026
Merged

feat(settings): module-owned settings screen with change password, biometrics, and autofill#60
OffRange merged 27 commits into
v2from
feat/settings

Conversation

@OffRange

@OffRange OffRange commented Jun 10, 2026

Copy link
Copy Markdown
Owner

Summary

Introduces the :feature:settings module with a module-owned navigation graph backing the Settings tab, replacing the previous placeholder.

  • Settings screen built on a new group-entry DSL: autofill toggle, biometric unlock toggle (hidden when biometrics are unavailable), change password, export/import data actions, third-party licenses, report issue, and app version.
  • Change password flow (ChangePasswordUseCase + ViewModel/Screen) with password or biometric reauthentication; transient key material (ARK, derived KEKs) is zeroized in finally blocks, and key derivation now suspends on Dispatchers.Default.
  • Autofill: new AutofillServiceRepository with system-state refresh; enable requests open the system autofill selection, disable requests stop the service.
  • Biometrics: enrollment/disable via BiometricCryptoController, failures surfaced as snackbars unless user-dismissed.
  • Tests: ViewModel, use case, and DSL unit tests; fakes provided via testFixtures (FakeAutofillServiceRepository in :feature:autofill, FakeAppVersionRepository in new :feature:settings fixtures).

Test plan

  • :rust:testDebugUnitTest, :core:identity:testDebugUnitTest, :feature:settings:testDebugUnitTest pass locally
  • CI green on full build

🤖 Generated with Claude Code

OffRange and others added 25 commits June 5, 2026 15:04
Replace the Text("SETTINGS") placeholder with the real SettingsScreen and add
the :feature:settings dependency. Additionally, remove the unused connectivity
route placeholder.
Allows getting enabled state
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…faithful

ChangePasswordUseCase now passes the recovered ARK directly to the wrapper and
scrubs that single array, instead of wrapping a copy and scrubbing only the
original — the copy variant left an un-zeroed ARK lingering in the heap.

Root cause: FakeKeyWrapper aliased the key arrays it was given (stored and
returned the same reference), unlike the real UniFFI wrapper which copies key
material across the FFI boundary and returns fresh arrays. Make the fake's
wrap/unwrap defensively copy so it matches real behavior and callers can scrub
recovered keys safely. UnlockWithPasswordUseCaseTest relied on that aliasing via
a ByteArray assertEquals (reference equality); switch it to assertContentEquals.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add the presentation layer for the change-master-password feature: state
(TextFieldState fields, field errors, password score), one-shot events, and
a ViewModel that orchestrates ChangePasswordUseCase for both password and
biometric reauthentication paths.

Wire the missing missingDimensionStrategy and core:item / testFixtures
dependencies so the settings module's unit tests resolve the flavored
transitive dependency graph.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…route

Introduce SettingsGraphRoute/SettingsHomeRoute/ChangePasswordRoute and a
NavGraphBuilder.settingsGraph() builder so :feature:settings owns its nested
navigation graph (settings list + change-password screen). Wire the existing
"Reset password" row to emit NavigateToChangePassword, which the screen forwards
to onOpenChangePassword. Add the serialization plugin and navigation-compose to
the module so the @serializable routes and graph builder compile.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Make the app's sealed RouteDestination extend the open core.ui.RouteDestination
and drop its Settings object; retype AppDestinations.route and NavigationWrapper
to the open interface so the Settings tab can be backed by the module-owned
SettingsGraphRoute. Replace the single Settings composable in MainActivity with
settingsGraph(...), wiring change-password navigation and up/back.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Add a new "Backup" section to the settings screen featuring export and import
data actions. This includes updating the ViewModel, defining new UI events, and
adding the required string resources.
Wipe the caller-supplied ARK and both derived KEKs in finally blocks so
no key copy survives an early return or a failed re-wrap. Key
derivation now suspends and runs on Dispatchers.Default instead of
blocking the caller thread.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Enable requests now open the system autofill selection while disable
requests stop the service and refresh state (the branches were
swapped). The biometrics toggle is hidden when biometrics are
unavailable, enrollment failures surface a snackbar unless the user
dismissed the prompt, export/import callbacks are actually invoked,
and the event channel is buffered so taps made while a prompt is open
are not dropped. Adds a placeholder Connectivity route.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Move FakeAutofillServiceRepository into :feature:autofill testFixtures
and add FakeAppVersionRepository to new :feature:settings testFixtures,
replacing the fakes previously defined inline in SettingsViewModelTest.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings June 10, 2026 17:43
@OffRange OffRange linked an issue Jun 10, 2026 that may be closed by this pull request
7 tasks

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR introduces a new :feature:settings module that owns the Settings tab navigation graph and UI, and adds a change-password flow with optional biometric reauthentication. It also updates autofill/biometric plumbing and refactors account observation to use a Flow-based API.

Changes:

  • Added :feature:settings with a module-owned settings graph, composable Settings UI DSL, and change-password screen/ViewModel.
  • Implemented ChangePasswordUseCase with key derivation off the main thread and explicit scrubbing of transient key material.
  • Added autofill service state querying/disable repository + updated biometric error modeling/mapping and account repository observation.

Reviewed changes

Copilot reviewed 55 out of 57 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
settings.gradle.kts Includes the new :feature:settings module in the build.
rust/src/testFixtures/kotlin/de/davis/keygo/rust/FakeKeyWrapper.kt Fixes test fixture key aliasing by copying key material in/out.
rust/src/main/kotlin/de/davis/keygo/rust/derive/KeyDeriver.kt Makes password KEK derivation suspend and moves it to Dispatchers.Default.
gradle/libs.versions.toml Adds AboutLibraries version, library, and plugin alias entries.
feature/settings/src/testFixtures/kotlin/de/davis/keygo/core/feature/settings/FakeAppVersionRepository.kt Adds settings test fixture for app version lookup.
feature/settings/src/test/kotlin/de/davis/keygo/feature/settings/presentation/SettingsViewModelTest.kt Adds SettingsViewModel unit tests.
feature/settings/src/test/kotlin/de/davis/keygo/feature/settings/presentation/component/SettingsDslTest.kt Adds unit tests for the settings list DSL.
feature/settings/src/test/kotlin/de/davis/keygo/feature/settings/presentation/changepassword/ChangePasswordViewModelTest.kt Adds ChangePasswordViewModel tests including biometric fallback flows.
feature/settings/src/main/res/values/strings.xml Adds settings and change-password UI strings.
feature/settings/src/main/kotlin/de/davis/keygo/feature/settings/presentation/SettingsViewModel.kt Implements SettingsViewModel combining account + OS-polled state and emitting UI events.
feature/settings/src/main/kotlin/de/davis/keygo/feature/settings/presentation/SettingsUiState.kt Defines settings screen UI state model.
feature/settings/src/main/kotlin/de/davis/keygo/feature/settings/presentation/SettingsUiEvent.kt Defines UI events originating from the settings UI.
feature/settings/src/main/kotlin/de/davis/keygo/feature/settings/presentation/SettingsScreen.kt Wires settings UI to navigation/actions, biometric enrollment adapter, and autofill intent.
feature/settings/src/main/kotlin/de/davis/keygo/feature/settings/presentation/SettingsRoutes.kt Adds a module-owned navigation graph for settings + change password.
feature/settings/src/main/kotlin/de/davis/keygo/feature/settings/presentation/SettingsEvent.kt Defines one-shot events emitted by SettingsViewModel.
feature/settings/src/main/kotlin/de/davis/keygo/feature/settings/presentation/SettingsContent.kt Builds the settings list sections/rows using the new DSL.
feature/settings/src/main/kotlin/de/davis/keygo/feature/settings/presentation/component/SettingsList.kt Implements the LazyColumn-based settings list renderer.
feature/settings/src/main/kotlin/de/davis/keygo/feature/settings/presentation/component/SettingsEntry.kt Defines settings row entry models (toggle/action/value).
feature/settings/src/main/kotlin/de/davis/keygo/feature/settings/presentation/component/SettingsDsl.kt Adds the settings DSL for grouping entries into sections.
feature/settings/src/main/kotlin/de/davis/keygo/feature/settings/presentation/changepassword/ChangePasswordViewModel.kt Implements change-password logic with optional biometric reauth and error handling.
feature/settings/src/main/kotlin/de/davis/keygo/feature/settings/presentation/changepassword/ChangePasswordState.kt Defines change-password screen state and events.
feature/settings/src/main/kotlin/de/davis/keygo/feature/settings/presentation/changepassword/ChangePasswordScreen.kt Implements change-password UI + biometric prompt integration.
feature/settings/src/main/kotlin/de/davis/keygo/feature/settings/domain/repository/AppVersionRepository.kt Adds app-version abstraction for settings.
feature/settings/src/main/kotlin/de/davis/keygo/feature/settings/domain/model/OsState.kt Adds OS-polled state model for settings (autofill/biometrics availability).
feature/settings/src/main/kotlin/de/davis/keygo/feature/settings/di/FeatureSettingsModule.kt Registers Koin module/component scan for settings feature.
feature/settings/src/main/kotlin/de/davis/keygo/feature/settings/data/repository/AppVersionRepositoryImpl.kt Implements version name retrieval via PackageManager.
feature/settings/consumer-rules.pro Adds consumer ProGuard rules file for the new module.
feature/settings/build.gradle.kts Adds module build config, dependencies, and enables test fixtures.
feature/credentials/src/main/kotlin/de/davis/keygo/feature/credentials/presentation/auth/SessionAuthState.kt Updates biometric error mapping to the new semantic BiometricAuthError model.
feature/credentials/build.gradle.kts Removes direct core.security dependency (now exposed via core.identity).
feature/autofill/src/testFixtures/kotlin/de/davis/keygo/core/feature/autofill/FakeAutofillServiceRepository.kt Adds autofill repository fake for tests.
feature/autofill/src/main/kotlin/de/davis/keygo/feature/autofill/domain/repository/AutofillServiceRepository.kt Introduces autofill service state/disable abstraction.
feature/autofill/src/main/kotlin/de/davis/keygo/feature/autofill/di/AutofillModule.kt Provides AutofillManager via DI.
feature/autofill/src/main/kotlin/de/davis/keygo/feature/autofill/data/repository/AutofillServiceRepositoryImpl.kt Implements autofill service repository via AutofillManager.
feature/autofill/build.gradle.kts Removes direct core.security dependency.
feature/auth/build.gradle.kts Removes direct core.security dependency and enables Kotlin serialization plugin.
core/security/src/test/kotlin/de/davis/keygo/core/security/presentation/BiometricAuthErrorFromTest.kt Adds unit tests for biometric error code mapping.
core/security/src/main/kotlin/de/davis/keygo/core/security/presentation/BiometricCryptoControllerImpl.kt Centralizes mapping from BiometricPrompt error codes to semantic errors.
core/security/src/main/kotlin/de/davis/keygo/core/security/domain/model/BiometricAuthError.kt Reworks biometric error model into semantic cases (Declined, LockedOut, Canceled, Unknown, etc.).
core/identity/src/testFixtures/kotlin/de/davis/keygo/core/identity/FakeAccountRepository.kt Updates fake repository to support observe(): Flow<Account?>.
core/identity/src/test/kotlin/de/davis/keygo/core/identity/domain/usecase/UnlockWithPasswordUseCaseTest.kt Fixes array equality assertion for ARK comparison.
core/identity/src/test/kotlin/de/davis/keygo/core/identity/domain/usecase/ChangePasswordUseCaseTest.kt Adds unit tests for change-password behavior and key scrubbing.
core/identity/src/main/kotlin/de/davis/keygo/core/identity/presentation/BiometricEnrollmentAdapterImpl.kt Adds biometric enrollment adapter to wrap/store ARK via biometric crypto controller.
core/identity/src/main/kotlin/de/davis/keygo/core/identity/presentation/BiometricEnrollmentAdapter.kt Adds enrollment adapter interface and helper for calling member-extension APIs.
core/identity/src/main/kotlin/de/davis/keygo/core/identity/domain/usecase/ChangePasswordUseCase.kt Adds the change-password use case with password/biometric reauth and scrubbing.
core/identity/src/main/kotlin/de/davis/keygo/core/identity/domain/repository/AccountRepository.kt Adds observe(): Flow<Account?> to account repository API.
core/identity/src/main/kotlin/de/davis/keygo/core/identity/domain/model/Reauthentication.kt Adds reauthentication model for change-password paths.
core/identity/src/main/kotlin/de/davis/keygo/core/identity/domain/model/ChangePasswordError.kt Adds domain error types for change-password use case.
core/identity/src/main/kotlin/de/davis/keygo/core/identity/domain/model/BiometricEnrollmentError.kt Adds error types for biometric enrollment adapter.
core/identity/src/main/kotlin/de/davis/keygo/core/identity/data/repository/AccountRepositoryImpl.kt Implements observe() and updates getOrNull() to use the flow.
core/identity/build.gradle.kts Changes core.security dependency to api to expose security types to consumers.
build.gradle.kts Adds AboutLibraries Gradle plugin alias (apply false).
app/src/main/kotlin/de/davis/keygo/core/presentation/model/RouteDestination.kt Aligns app-level RouteDestination with core.ui.RouteDestination and adds Libraries destination.
app/src/main/kotlin/de/davis/keygo/app/presentation/MainActivity.kt Integrates settings graph and AboutLibraries screen into the app NavHost.
app/src/main/kotlin/de/davis/keygo/app/presentation/component/NavigationWrapper.kt Switches to the core.ui.RouteDestination type.
app/src/main/kotlin/de/davis/keygo/app/presentation/AppDestinations.kt Points Settings tab destination to SettingsGraphRoute.
app/build.gradle.kts Applies AboutLibraries plugin, adds dependency, and includes :feature:settings.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread rust/src/main/kotlin/de/davis/keygo/rust/derive/KeyDeriver.kt
…ault

Update the UI state directly in SettingsViewModel when disabling autofill to prevent race conditions with OS polling. Additionally, adjust FakeAutofillServiceRepository to better mirror real-world asynchronous propagation and change the default biometricsAvailable state to false.
@OffRange OffRange merged commit 2237739 into v2 Jun 10, 2026
8 checks passed
@OffRange OffRange deleted the feat/settings branch June 10, 2026 18:50
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.

Reimplement Settings screen

2 participants