Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
27ed363
chore: add CLAUDE.md with project overview and development guidelines
claude Apr 12, 2026
b6b3799
chore: add Compose Preview guideline to CLAUDE.md
claude Apr 12, 2026
712d4e4
#2105 don't wrap tile labels on Discover screen
claude Apr 12, 2026
069cd08
Add Expert Mode debug screen for raw getevent output (issue #2081)
claude Apr 12, 2026
af5f558
#2045 Add 'Perform IME action' action (Android 13+)
claude Apr 12, 2026
b79c0c4
#2045 Use inputMethod.currentInputConnection.performEditorAction()
claude Apr 12, 2026
06d40c9
Merge pull request #2109 from keymapperorg/claude/create-claude-md-UbIX0
sds100 Apr 12, 2026
0c04c9f
#2045 Use Keyboard icon for PerformImeAction (fix build)
claude Apr 12, 2026
6c0cc61
#2045 Fix missing PERFORM_IME_ACTION case in CreateActionDelegate
claude Apr 12, 2026
08ea2c4
#2106 fix: show dialog when using in-app keyboard switcher to disable…
claude Apr 12, 2026
bfb0d8a
#2106 refactor: move auto-switch IME logic to ShowInputMethodPickerUs…
claude Apr 13, 2026
2887338
fix: title in AdvancedTriggersScreen would not wrap correctly
sds100 Apr 13, 2026
96e5033
delete modules.xml
sds100 Apr 13, 2026
2e63fa6
Merge remote-tracking branch 'origin/claude/issue-2106-auto-switch-im…
sds100 Apr 13, 2026
5b48de9
#2045 rename action
sds100 Apr 13, 2026
913bb8a
Merge pull request #2111 from keymapperorg/claude/affectionate-einste…
sds100 Apr 13, 2026
22f9892
chore: update changelog
sds100 Apr 13, 2026
4ac7fc2
Merge pull request #2110 from keymapperorg/claude/don-t-wrap-strings-…
sds100 Apr 14, 2026
88dedb3
New Crowdin translations by GitHub Action
crowdin-bot Apr 14, 2026
9567116
Merge branch 'develop' into claude/2081-getevent-debug-screen
sds100 Apr 14, 2026
297c8ef
Fix ktlint violations in GetEventViewModel and GetEventScreen
claude Apr 14, 2026
ddb95d4
#2081 fix padding and text style
sds100 Apr 14, 2026
597bcde
Merge pull request #2112 from keymapperorg/l10n/develop
sds100 Apr 14, 2026
970f30c
Merge remote-tracking branch 'origin/claude/2081-getevent-debug-scree…
sds100 Apr 14, 2026
567150a
#2091 fix: show error for device assistant action when no assistant i…
claude Apr 14, 2026
498a559
#2091 fix: add missing isSuccess import in ActionErrorSnapshot
claude Apr 14, 2026
e178bbf
Merge pull request #2117 from keymapperorg/claude/issue-2091-device-a…
sds100 May 5, 2026
4f1648d
#2081 create tabs for info and events
sds100 May 5, 2026
0968f4d
#2081 show refresh button for getevent info
sds100 May 5, 2026
dc6a326
#2081 add getevent files to provider
sds100 May 5, 2026
6532c66
#2081 save getevent output and show it even if system bridge disabled
sds100 May 5, 2026
a797400
Merge pull request #2116 from keymapperorg/claude/2081-getevent-debug…
sds100 May 5, 2026
48694b5
Update CHANGELOG with new entries
sds100 May 5, 2026
b39cc9d
#1956 fix emergency kill timing source
sds100 May 5, 2026
166f751
#1029 feat: add action to show a toast message
sds100 May 5, 2026
a3b50db
Merge branch 'develop' into feature/1029-toast-action
sds100 May 5, 2026
6fc8801
tell AI to update CHANGELOG.md when making a PR
sds100 May 5, 2026
f4addba
#1956 reformat rust
sds100 May 5, 2026
39ee0b1
#1029 fix tests
sds100 May 5, 2026
0c6161a
#2087 fix: small segmented button text is not readable in dark mode
sds100 May 5, 2026
a8dd95b
#2077 feat: rename "This device" and "any device" to "This Android de…
sds100 May 5, 2026
19a1b64
#2107 clarify accessibility restart dialog actions and copy
sds100 May 5, 2026
a3f39ab
Merge pull request #2132 from keymapperorg/feature/1029-toast-action
sds100 May 5, 2026
54adb8f
Merge pull request #2133 from keymapperorg/fix/1956-system-bridge-eme…
sds100 May 5, 2026
88e80da
#2107 update changelog entry
sds100 May 5, 2026
28c437b
update CLAUDE.md and cursor rules
sds100 May 5, 2026
b04ea7c
#2067 feat: add select all text action
sds100 May 5, 2026
119d09d
Merge pull request #2134 from keymapperorg/feature/2107-accessibility…
sds100 May 5, 2026
c571ce6
Merge branch 'develop' into feature/2067-select-all-text
sds100 May 5, 2026
5665145
bump version to 4.1.0
sds100 May 5, 2026
15b245d
Merge pull request #2135 from keymapperorg/feature/2067-select-all-text
sds100 May 5, 2026
1e19635
update changelog release date
sds100 May 5, 2026
384ba77
Merge remote-tracking branch 'origin/develop' into develop
sds100 May 5, 2026
a4b36ac
move getevent debug screen to expert mode section and hide events out…
sds100 May 5, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions .cursor/rules/keymapper-add-action-checklist.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
---
description: Required checklist when adding a new Action
alwaysApply: true
---

# Adding A New Action

Before implementing, confirm whether the action is editable.
- Branch names must follow `feature/123-branch-name` or `fix/123-branch-name`.

Then follow this sequence:

1. Add an ID in `ActionId`.
2. Add a new `ActionData` sealed class variant.
3. Map entity conversions in `ActionDataEntityMapper`.
4. Assign category in `ActionUtils`.
5. If editable, add it to `ActionUtils.isEditable`.
6. Add title string in `strings.xml`.
7. Add title and Compose icon in `ActionUtils` (ignore drawables).
8. Add title in `ActionUiHelper`.
9. Add execution handling in `PerformActionsUseCase`.
10. Handle creation in `CreateActionDelegate`.

Do not delete existing action code; follow existing names and place additions near similar actions.
15 changes: 15 additions & 0 deletions .cursor/rules/keymapper-architecture.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
description: Module boundaries and architectural conventions for KeyMapper
alwaysApply: true
---

# KeyMapper Architecture

- Branch names must follow `feature/123-branch-name` or `fix/123-branch-name`.
- Treat the codebase as Clean Architecture + MVVM with unidirectional data flow.
- Keep dependency direction as `:app` -> `:base` -> `:common`/`:data`/`:system`/`:sysbridge`.
- Keep domain models in `:common` and persistence entities in `:data` with explicit mappers.
- Put business logic in use cases and inject them into ViewModels.
- Expose UI state via `Flow`/`StateFlow`; collect from UI and send events back to ViewModels.
- Use Hilt for DI and preserve app-specific binding overrides in app modules.
- Use coroutines for async work and `viewModelScope` for ViewModel-owned jobs.
16 changes: 16 additions & 0 deletions .cursor/rules/keymapper-build-and-tooling.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
description: Build, test, and tooling commands for KeyMapper
alwaysApply: true
---

# KeyMapper Build And Tooling

- Branch names must follow `feature/123-branch-name` or `fix/123-branch-name`.
- Use these common commands:
- `./gradlew assembleDebug`
- `./gradlew test`
- `./gradlew connectedAndroidTest`
- `./gradlew ktlintCheck` or `./gradlew ktlintFormat`
- Release builds require `KEYSTORE_PASSWORD` and `KEY_PASSWORD` environment variables.
- Target platform constraints: min SDK 26, target SDK 36, Java 11.
- Dependency versions are managed in `gradle/libs.versions.toml`.
34 changes: 34 additions & 0 deletions .cursor/rules/keymapper-commit-messages.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
---
description: Commit message format convention for KeyMapper
alwaysApply: true
---

# KeyMapper Commit Messages

Use this format for commits:

`#<issue_number> <type>: <description>`

Allowed types:

- `feat`
- `fix`
- `chore`
- `refactor`
- `style`

Example:

`#2025 feat: add button to report bug on home screen`

# Branch Naming Convention

- Use `feature/123-branch-name` for new features.
- Use `fix/123-branch-name` for bug fixes.

# Pull Request Changelog Requirement

- When preparing a PR, update `CHANGELOG.md` for any user-facing fix/feature/change or notable
internal change.
- If no changelog entry is needed (for example, docs-only or CI-only), explicitly note that in the PR
description.
16 changes: 16 additions & 0 deletions .cursor/rules/keymapper-compose-guidelines.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
description: Compose coding conventions used in KeyMapper
globs: "**/*.kt"
alwaysApply: false
---

# KeyMapper Compose Guidelines

Apply this when editing Compose code:

- Branch names must follow `feature/123-branch-name` or `fix/123-branch-name`.
- Put `Modifier` as the first optional parameter after required parameters.
- For non-standard colors, use `LocalCustomColorsPalette` instead of `MaterialTheme.colorScheme`.
- Check `KeyMapperIcons` before introducing icons from other sources.
- Use `LocalUriHandler.openUriSafe` for URL launching and do not hoist URL launching logic.
- Prefer imports; avoid fully qualified names in Compose call sites.
19 changes: 19 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,22 @@
## [4.1.0](https://github.com/sds100/KeyMapper/releases/tag/v4.1.0)

#### 05 May 2026

## Added

- #2067 add action to select all text in the focused field.
- #2045 add action to input on-screen keyboard enter/send button.
- #2106 disable the keyboard auto-switching setting when manually switching the keyboard in the Key Mapper homescreen menu.
- #1029 add action to show a toast message.
- #2081 add getevent debug screen.
- #2087 small segmented button text is not readable in dark mode.
- #2077 rename "This device" and "any device" to "This Android device" and "Any input device" to prevent confusion.

## Fixed

- #2091 show an error on the "open device assistant" action when no device assistant is installed.
- #2107 clarify the crashed accessibility service dialog text and keep only Cancel/Restart actions.

## [4.0.5](https://github.com/sds100/KeyMapper/releases/tag/v4.0.5)

#### 26 February 2026
Expand Down
109 changes: 109 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
# KeyMapper

## Project Overview

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

- **Package:** `io.github.sds100.keymapper`
- **Languages:** Kotlin (primary), Rust (event device handling), C++ (system bridge JNI)

## Build Commands

```bash
./gradlew assembleDebug # debug APK
./gradlew assembleCi # CI build (minified)
./gradlew assembleRelease # release build (requires KEYSTORE_PASSWORD and KEY_PASSWORD env vars)
./gradlew clean # clean build outputs
```

## Test & Lint Commands

```bash
# Kotlin unit tests
./gradlew testDebugUnitTest
./gradlew :base:testDebugUnitTest # single module

# Kotlin lint
./gradlew ktlintCheck # check
./gradlew ktlintFormat # auto-fix

# Rust (run from evdev/src/main/rust/evdev_manager)
cargo fmt --check
cargo test --package evdev_manager_core
```

CI runs all of the above on every PR (see `.github/workflows/pull-request.yml`).

## Architecture & Modules

Multi-module Gradle project using MVVM, Hilt DI, Room, StateFlow/Flow, Jetpack Compose, and Coroutines.

| Module | Responsibility |
|---|---|
| `app/` | Entry point: `MainActivity`, `KeyMapperApp`, `MyAccessibilityService` |
| `base/` | UI screens and ViewModels (actions, constraints, triggers, keymaps, settings) |
| `common/` | Shared utilities and models |
| `data/` | Room database, repositories, DataStore, migrations |
| `system/` | Device APIs, permission management |
| `sysbridge/` | C++ JNI via CMake for elevated system access |
| `evdev/` | Rust event device handling + JNI bindings |
| `api/` | AIDL public interface definitions |
| `systemstubs/` | Mock stubs for system classes used in tests |

Most business logic lives in `base/src/main/java/io/github/sds100/keymapper/`.

## Code Style

- **Kotlin:** ktlint with android_studio code style (configured via `.editorconfig`)
- No function expression bodies
- Trailing commas allowed
- Run `./gradlew ktlintFormat` before committing
- **Rust:** `cargo fmt`

## Commit Message Convention

```
#<issue_number> <type>: <description>
```

Types: `feat`, `fix`, `chore`, `refactor`, `style`

Example: `#2025 feat: add button to report bug on home screen`

## Branch Naming Convention

- Use `feature/123-branch-name` for new features.
- Use `fix/123-branch-name` for bug fixes.

## Pull Request Requirement

- Any user-facing behavior change, fix, feature, or notable internal change in a PR must include an
update to `CHANGELOG.md`.
- If there is truly no changelog impact (for example, docs-only or CI-only changes), explicitly state
that in the PR description.

## Compose Guidelines

- `Modifier` is always the first parameter after required params
- Use `LocalCustomColorsPalette` (not `MaterialTheme.colorScheme`) for non-standard colors
- Check `KeyMapperIcons` before using other icon sources
- Use `LocalUriHandler.openUriSafe` extension for URL launching — do not hoist URL launching logic up the call stack
- Use import statements; never use fully qualified names in Compose code
- Write `@Preview` composables for every screen

## Adding a New Action (10-step checklist)

First determine whether the action is editable. Then:

1. Add a new ID to `ActionId`
2. Create a new `ActionData` sealed class variant
3. Map to/from entity in `ActionDataEntityMapper`
4. Assign a category in `ActionUtils`
5. If editable, add to `isEditable` in `ActionUtils`
6. Add a title string in `strings.xml`
7. Add title and Compose icon in `ActionUtils` (ignore drawables)
8. Add title in `ActionUiHelper`
9. Stub out execution in `PerformActionsUseCase`
10. Handle creation in `CreateActionDelegate`

Do not delete existing action code. Follow existing naming conventions exactly. Add new code near similar existing actions.
4 changes: 2 additions & 2 deletions app/version.properties
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
VERSION_NAME=4.0.5
VERSION_CODE=247
VERSION_NAME=4.1.0
VERSION_CODE=248
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import io.github.sds100.keymapper.base.actions.uielement.InteractUiElementScreen
import io.github.sds100.keymapper.base.actions.uielement.InteractUiElementViewModel
import io.github.sds100.keymapper.base.constraints.ChooseConstraintScreen
import io.github.sds100.keymapper.base.constraints.ChooseConstraintViewModel
import io.github.sds100.keymapper.base.debug.GetEventScreen
import io.github.sds100.keymapper.base.expertmode.ExpertModeScreen
import io.github.sds100.keymapper.base.expertmode.ExpertModeSetupScreen
import io.github.sds100.keymapper.base.logging.LogScreen
Expand Down Expand Up @@ -165,6 +166,14 @@ fun BaseMainNavHost(
)
}

composable<NavDestination.GetEvent> {
GetEventScreen(
modifier = Modifier.fillMaxSize(),
viewModel = hiltViewModel(),
onBackClick = { navController.popBackStack() },
)
}

composable<NavDestination.ChooseSetting> {
ChooseSettingScreen(
modifier = Modifier.fillMaxSize(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ import io.github.sds100.keymapper.base.constraints.ConfigConstraintsUseCaseImpl
import io.github.sds100.keymapper.base.constraints.CreateConstraintUseCase
import io.github.sds100.keymapper.base.constraints.CreateConstraintUseCaseImpl
import io.github.sds100.keymapper.base.constraints.DisplayConstraintUseCase
import io.github.sds100.keymapper.base.debug.GetEventOutputUseCase
import io.github.sds100.keymapper.base.debug.GetEventOutputUseCaseImpl
import io.github.sds100.keymapper.base.expertmode.ExpertModeSetupDelegateImpl
import io.github.sds100.keymapper.base.expertmode.SystemBridgeSetupDelegate
import io.github.sds100.keymapper.base.expertmode.SystemBridgeSetupUseCase
Expand Down Expand Up @@ -185,6 +187,10 @@ abstract class BaseViewModelHiltModule {
@ViewModelScoped
abstract fun bindShareLogcatUseCase(impl: ShareLogcatUseCaseImpl): ShareLogcatUseCase

@Binds
@ViewModelScoped
abstract fun bindGetEventOutputUseCase(impl: GetEventOutputUseCaseImpl): GetEventOutputUseCase

@Binds
@ViewModelScoped
abstract fun bindOnboardingTipDelegate(impl: OnboardingTipDelegateImpl): OnboardingTipDelegate
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -833,6 +833,11 @@ sealed class ActionData : Comparable<ActionData> {
override val id = ActionId.SHOW_KEYBOARD_PICKER
}

@Serializable
data object PerformImeAction : ActionData() {
override val id = ActionId.PERFORM_IME_ACTION
}

@Serializable
data object CopyText : ActionData() {
override val id = ActionId.TEXT_COPY
Expand All @@ -853,6 +858,11 @@ sealed class ActionData : Comparable<ActionData> {
override val id = ActionId.SELECT_WORD_AT_CURSOR
}

@Serializable
data object SelectAllText : ActionData() {
override val id = ActionId.SELECT_ALL_TEXT
}

@Serializable
data object VoiceAssistant : ActionData() {
override val id = ActionId.OPEN_VOICE_ASSISTANT
Expand Down Expand Up @@ -919,6 +929,22 @@ sealed class ActionData : Comparable<ActionData> {
}
}

@Serializable
data class Toast(val message: String, val duration: Duration) : ActionData() {
override val id: ActionId = ActionId.TOAST

@Serializable
enum class Duration {
SHORT,
LONG,
}

override fun compareTo(other: ActionData) = when (other) {
is Toast -> compareValuesBy(this, other, { it.message }, { it.duration })
else -> super.compareTo(other)
}
}

@Serializable
data object AnswerCall : ActionData() {
override val id: ActionId = ActionId.ANSWER_PHONE_CALL
Expand Down
Loading
Loading