|
| 1 | +# KeyMapper |
| 2 | + |
| 3 | +## Project Overview |
| 4 | + |
| 5 | +Android app for custom key/gamepad remapping, macros, and on-screen buttons. Supports Android 8.0+ (minSdk 26), targets API 36. Distributed on Google Play and F-Droid (FOSS flavor). |
| 6 | + |
| 7 | +- **Package:** `io.github.sds100.keymapper` |
| 8 | +- **Languages:** Kotlin (primary), Rust (event device handling), C++ (system bridge JNI) |
| 9 | + |
| 10 | +## Build Commands |
| 11 | + |
| 12 | +```bash |
| 13 | +./gradlew assembleDebug # debug APK |
| 14 | +./gradlew assembleCi # CI build (minified) |
| 15 | +./gradlew assembleRelease # release build (requires KEYSTORE_PASSWORD and KEY_PASSWORD env vars) |
| 16 | +./gradlew clean # clean build outputs |
| 17 | +``` |
| 18 | + |
| 19 | +## Test & Lint Commands |
| 20 | + |
| 21 | +```bash |
| 22 | +# Kotlin unit tests |
| 23 | +./gradlew testDebugUnitTest |
| 24 | +./gradlew :base:testDebugUnitTest # single module |
| 25 | + |
| 26 | +# Kotlin lint |
| 27 | +./gradlew ktlintCheck # check |
| 28 | +./gradlew ktlintFormat # auto-fix |
| 29 | + |
| 30 | +# Rust (run from evdev/src/main/rust/evdev_manager) |
| 31 | +cargo fmt --check |
| 32 | +cargo test --package evdev_manager_core |
| 33 | +``` |
| 34 | + |
| 35 | +CI runs all of the above on every PR (see `.github/workflows/pull-request.yml`). |
| 36 | + |
| 37 | +## Architecture & Modules |
| 38 | + |
| 39 | +Multi-module Gradle project using MVVM, Hilt DI, Room, StateFlow/Flow, Jetpack Compose, and Coroutines. |
| 40 | + |
| 41 | +| Module | Responsibility | |
| 42 | +|---|---| |
| 43 | +| `app/` | Entry point: `MainActivity`, `KeyMapperApp`, `MyAccessibilityService` | |
| 44 | +| `base/` | UI screens and ViewModels (actions, constraints, triggers, keymaps, settings) | |
| 45 | +| `common/` | Shared utilities and models | |
| 46 | +| `data/` | Room database, repositories, DataStore, migrations | |
| 47 | +| `system/` | Device APIs, permission management | |
| 48 | +| `sysbridge/` | C++ JNI via CMake for elevated system access | |
| 49 | +| `evdev/` | Rust event device handling + JNI bindings | |
| 50 | +| `api/` | AIDL public interface definitions | |
| 51 | +| `systemstubs/` | Mock stubs for system classes used in tests | |
| 52 | + |
| 53 | +Most business logic lives in `base/src/main/java/io/github/sds100/keymapper/`. |
| 54 | + |
| 55 | +## Code Style |
| 56 | + |
| 57 | +- **Kotlin:** ktlint with android_studio code style (configured via `.editorconfig`) |
| 58 | + - No function expression bodies |
| 59 | + - Trailing commas allowed |
| 60 | + - Run `./gradlew ktlintFormat` before committing |
| 61 | +- **Rust:** `cargo fmt` |
| 62 | + |
| 63 | +## Commit Message Convention |
| 64 | + |
| 65 | +``` |
| 66 | +#<issue_number> <type>: <description> |
| 67 | +``` |
| 68 | + |
| 69 | +Types: `feat`, `fix`, `chore`, `refactor`, `style` |
| 70 | + |
| 71 | +Example: `#2025 feat: add button to report bug on home screen` |
| 72 | + |
| 73 | +## Compose Guidelines |
| 74 | + |
| 75 | +- `Modifier` is always the first parameter after required params |
| 76 | +- Use `LocalCustomColorsPalette` (not `MaterialTheme.colorScheme`) for non-standard colors |
| 77 | +- Check `KeyMapperIcons` before using other icon sources |
| 78 | +- Use `LocalUriHandler.openUriSafe` extension for URL launching — do not hoist URL launching logic up the call stack |
| 79 | +- Use import statements; never use fully qualified names in Compose code |
| 80 | +- Write `@Preview` composables for every screen |
| 81 | + |
| 82 | +## Adding a New Action (10-step checklist) |
| 83 | + |
| 84 | +First determine whether the action is editable. Then: |
| 85 | + |
| 86 | +1. Add a new ID to `ActionId` |
| 87 | +2. Create a new `ActionData` sealed class variant |
| 88 | +3. Map to/from entity in `ActionDataEntityMapper` |
| 89 | +4. Assign a category in `ActionUtils` |
| 90 | +5. If editable, add to `isEditable` in `ActionUtils` |
| 91 | +6. Add a title string in `strings.xml` |
| 92 | +7. Add title and Compose icon in `ActionUtils` (ignore drawables) |
| 93 | +8. Add title in `ActionUiHelper` |
| 94 | +9. Stub out execution in `PerformActionsUseCase` |
| 95 | +10. Handle creation in `CreateActionDelegate` |
| 96 | + |
| 97 | +Do not delete existing action code. Follow existing naming conventions exactly. Add new code near similar existing actions. |
0 commit comments