Files
kvitt/android/README.md
T

61 lines
3.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Kvitt Android project
One Gradle project, modules as in SPEC §6.2 and §14.7. Build only through `tools/gradle-serial.sh` with
`PATH=/opt/homebrew/opt/openjdk@25/bin:$PATH` (CLAUDE.md › Build and verify). The gate is `tools/gradle-serial.sh check`:
compile, unit tests (including every vector of `contracts/test-vectors/`), lint (`MissingTranslation` and
`ExtraTranslation` are errors), the architecture check and the screenshot tests.
## Modules
| Module | Plugin (build-logic) | Notes |
|---|---|---|
| `:core:model` | `kvitt.jvm.library` | UI contract (§14.2.4), domain types `model.money` (§14.5.1), analytics types `model.analytics` (§14.5.4) |
| `:domain` | `kvitt.jvm.library` | §14.5 functions; `VectorTest` runs every vector file, copied by `:domain:copyTestVectors` |
| `:core:design` | `kvitt.android.library` + `kvitt.android.compose` | `KvittTheme`, tokens, shared components, `Canvas` charts, formatters and shared strings (M6, `3080804`; SPEC §6.2); M6 owns the module |
| `:core:fakes` | `kvitt.android.library` | `FakeRepositories(scenario)`, `FakeScenarios.empty`; scenarios of feature X go in `fakes/<x>/` |
| `:data` | `kvitt.android.library` + KSP + Room 3 | Room entities and `KvittDatabase` v1, schema in `data/schemas/`; M5 owns the module |
| `:adapter:fcm` | `kvitt.android.library` | shell; M8 owns it |
| `:feature:*` | `kvitt.android.feature` | shells with one entry composable; M7a to M7f own them |
| `:app` | `kvitt.android.application` + compose + hilt + Roborazzi | Application, MainActivity, goldens in `app/src/test/screenshots/` |
## Architecture check
`architectureCheck` runs in every module's `check` (`build-logic/.../ArchitectureRules.kt` holds the rules as data).
It fails on a project dependency outside the §6.2 table, on `:core:fakes` in any configuration that is not a test or
debug configuration, and, in `:core:model` and `:domain`, on an Android plugin, on a library outside
`org.jetbrains.kotlin*`, and on any import or fully qualified reference outside `kotlin.*`, `kotlinx.*` and
`se.hagstedt.kvitt.*` in `src/main`. A new module must be added to `ArchitectureRules.allowed`.
## Feature convention
* Each feature module exposes **one composable entry point per screen**. By default it obtains its ViewModel with
`hiltViewModel()` (from `androidx.hilt:hilt-lifecycle-viewmodel-compose`) and takes navigation as plain lambdas,
for example:
```kotlin
@Composable
fun AccountDetailScreen(
onOpenExpense: (ExpenseId) -> Unit,
onAddExpense: (AccountId) -> Unit,
onBack: () -> Unit,
modifier: Modifier = Modifier,
viewModel: AccountDetailViewModel = hiltViewModel(),
)
```
A stateless overload that takes the `*State` and event lambdas is what previews and screenshot tests render.
* ViewModels are `@HiltViewModel` classes that receive repositories **through the interfaces of `:core:model`** only.
`:app` binds those interfaces to `:data` (release) or `:core:fakes` (debug and tests) with Hilt (M8).
* Features never reference another feature, the `NavHost` or `:data`/`:domain`. `:app` owns the `NavHost` and wires
the lambdas.
* Strings live in the feature's own `src/main/res/values/strings.xml` and `values-sv/strings.xml`, keys prefixed with
the feature name; shared strings in `:core:design`; `:app` holds only the app name.
* Previews and screenshot tests use `FakeRepositories(scenario)` from `:core:fakes` (`debugImplementation` and
`testImplementation`, added by the feature plugin). Fakes never reach a release build.
## Screenshot tests
Roborazzi on Robolectric. `roborazzi.test.verify=true` in `gradle.properties` makes every unit-test run compare with the
committed goldens. Re-record an intended change with `tools/gradle-serial.sh recordRoborazziDebug` and open every
changed PNG before committing.