Skip to content

Add configurable hardware key mappings#358

Open
not-linyi wants to merge 2 commits into
qiin2333:masterfrom
not-linyi:feature/hardware-key-mapping
Open

Add configurable hardware key mappings#358
not-linyi wants to merge 2 commits into
qiin2333:masterfrom
not-linyi:feature/hardware-key-mapping

Conversation

@not-linyi

Copy link
Copy Markdown

Summary

  • add per-device physical hardware key mappings using scan codes when available
  • add a settings UI to learn, list, delete, and clear mappings
  • support capturing system-reserved keys through the keyboard accessibility service
  • apply mappings to both normal keyboard input and accessibility-service input
  • add English and Simplified Chinese strings

Testing

  • ./gradlew.bat :app:testNonRootDebugUnitTest --tests "com.limelight.binding.input.HardwareKeyMappingStoreTest" ?
  • tested locally with physical hardware keys
  • Root debug unit-test compilation is currently blocked in untouched app/src/root/.../evdev sources by key constant/type errors, before this test runs

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.

⚠️ Not ready to approve

There are correctness/performance issues in the new input paths (notably event consumption when no accessibility callback is installed, and per-keystroke JSON parsing) that could break key handling or introduce latency.

Pull request overview

This PR adds per-device physical hardware key remapping (using scan codes when available), exposes a Settings UI to manage these mappings, and ensures mappings apply to both normal keyboard input and AccessibilityService-captured key events.

Changes:

  • Added HardwareKeyMappingStore + HardwareKeyMapping for persisting and applying per-device key remaps.
  • Integrated remapping into both KeyboardInputHandler (normal input path) and KeyboardAccessibilityService (reserved/system key capture path).
  • Added Settings entry/UI wiring plus new English and zh-CN strings, and a small unit test for JSON round-tripping and corrupt input handling.
File summaries
File Description
app/src/main/java/com/limelight/binding/input/HardwareKeyMappingStore.kt New mapping model + persistence + remap logic.
app/src/main/java/com/limelight/KeyboardInputHandler.kt Applies hardware remapping on key down/up before existing handling.
app/src/main/java/com/limelight/services/KeyboardAccessibilityService.kt Applies remapping for accessibility-captured key events and adds a “capture all keys” mode.
app/src/main/java/com/limelight/preferences/StreamSettings.kt Adds UI to learn/add/delete/clear mappings and hooks it into settings.
app/src/main/res/xml/preferences.xml Adds a new “Hardware key mappings” preference entry.
app/src/main/res/values/strings.xml Adds English strings for the new UI.
app/src/main/res/values-zh-rCN/strings.xml Adds Simplified Chinese strings for the new UI.
app/src/test/java/com/limelight/binding/input/HardwareKeyMappingStoreTest.kt Adds basic encode/decode and corrupt JSON tests.

Copilot's findings

  • Files reviewed: 8/8 changed files
  • Comments generated: 4

Note

Your feedback helps us improve the quality of this feature.
Please use 👍 or 👎 to tell us whether this assessment is correct.


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

Comment on lines +87 to +91
fun load(context: Context): List<HardwareKeyMapping> {
val json = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
.getString(KEY_MAPPINGS, null) ?: return emptyList()
return decode(json)
}
Comment on lines +112 to +123
val item = array.optJSONObject(index) ?: continue
val target = item.optInt("targetKeyCode", KeyEvent.KEYCODE_UNKNOWN)
if (target == KeyEvent.KEYCODE_UNKNOWN) continue
add(
HardwareKeyMapping(
deviceKey = item.optString("deviceKey"),
deviceName = item.optString("deviceName", "Unknown keyboard"),
sourceScanCode = item.optInt("sourceScanCode"),
sourceKeyCode = item.optInt("sourceKeyCode"),
targetKeyCode = target
)
)
Comment on lines 31 to +41
override fun onKeyEvent(event: KeyEvent): Boolean {
if (captureAllKeys && interceptingEnabled) {
keyEventCallback?.onKeyEvent(event)
return true
}

val mappedEvent = HardwareKeyMappingStore.remap(this, event)
if (mappedEvent !== event && interceptingEnabled) {
keyEventCallback?.onKeyEvent(mappedEvent)
return true
}
Comment on lines +247 to +267
fun showHardwareKeyMappings() {
val mappings = HardwareKeyMappingStore.load(this)
val labels = mappings.map(::mappingLabel).toMutableList()
labels.add(getString(R.string.hardware_key_mapping_add))
if (mappings.isNotEmpty()) labels.add(getString(R.string.hardware_key_mapping_clear))

AlertDialog.Builder(this)
.setTitle(R.string.title_hardware_key_mappings)
.setItems(labels.toTypedArray()) { _, index ->
when {
index < mappings.size -> confirmDeleteMapping(mappings[index])
index == mappings.size -> beginHardwareKeyCapture()
else -> {
HardwareKeyMappingStore.clear(this)
showHardwareKeyMappings()
}
}
}
.setNegativeButton(android.R.string.cancel, null)
.show()
}
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.

2 participants