From 106416a4fe91055cf1c3b2f7a800cabf83c40681 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 27 Jun 2026 00:37:09 +0000 Subject: [PATCH] #2174 feat: add default do not remap preference Add a "Do not remap by default" toggle to the Change Default Options settings page. When enabled, new trigger keys will have consumeEvent = false by default, letting the original key event pass through to other apps. --- CHANGELOG.md | 1 + .../keymaps/GetDefaultKeyMapOptionsUseCase.kt | 6 ++ .../settings/DefaultOptionsSettingsScreen.kt | 13 +++++ .../base/settings/SettingsViewModel.kt | 55 ++++++++++++------- .../base/trigger/ConfigTriggerDelegate.kt | 6 +- .../base/trigger/ConfigTriggerUseCase.kt | 2 + base/src/main/res/values/strings.xml | 3 + .../io/github/sds100/keymapper/data/Keys.kt | 1 + .../keymapper/data/PreferenceDefaults.kt | 2 + 9 files changed, 66 insertions(+), 23 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a2cb693525..366451a8ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ ## Added - #2140 Add monochrome app icon layer for themed icon support on Android 13+. +- #2174 Add "Do not remap by default" preference to the default options settings page. ## Fixed diff --git a/base/src/main/java/io/github/sds100/keymapper/base/keymaps/GetDefaultKeyMapOptionsUseCase.kt b/base/src/main/java/io/github/sds100/keymapper/base/keymaps/GetDefaultKeyMapOptionsUseCase.kt index 89fa437515..24d8fb2034 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/keymaps/GetDefaultKeyMapOptionsUseCase.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/keymaps/GetDefaultKeyMapOptionsUseCase.kt @@ -55,6 +55,11 @@ class GetDefaultKeyMapOptionsUseCaseImpl @Inject constructor( preferenceRepository.get(Keys.defaultVibrateDuration) .map { it ?: PreferenceDefaults.VIBRATION_DURATION } .stateIn(coroutineScope, SharingStarted.Lazily, PreferenceDefaults.VIBRATION_DURATION) + + override val defaultDoNotRemap: StateFlow = + preferenceRepository.get(Keys.defaultDoNotRemap) + .map { it ?: PreferenceDefaults.DO_NOT_REMAP } + .stateIn(coroutineScope, SharingStarted.Lazily, PreferenceDefaults.DO_NOT_REMAP) } interface GetDefaultKeyMapOptionsUseCase { @@ -65,4 +70,5 @@ interface GetDefaultKeyMapOptionsUseCase { val defaultDoublePressDelay: StateFlow val defaultSequenceTriggerTimeout: StateFlow val defaultVibrateDuration: StateFlow + val defaultDoNotRemap: StateFlow } diff --git a/base/src/main/java/io/github/sds100/keymapper/base/settings/DefaultOptionsSettingsScreen.kt b/base/src/main/java/io/github/sds100/keymapper/base/settings/DefaultOptionsSettingsScreen.kt index 2d807b2042..b36d4e4fc9 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/settings/DefaultOptionsSettingsScreen.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/settings/DefaultOptionsSettingsScreen.kt @@ -13,6 +13,7 @@ import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.rounded.ArrowBack +import androidx.compose.material.icons.rounded.Keyboard import androidx.compose.material3.BottomAppBar import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon @@ -35,6 +36,7 @@ import io.github.sds100.keymapper.base.utils.ui.SliderMaximums import io.github.sds100.keymapper.base.utils.ui.SliderMinimums import io.github.sds100.keymapper.base.utils.ui.SliderStepSizes import io.github.sds100.keymapper.base.utils.ui.compose.SliderOptionText +import io.github.sds100.keymapper.base.utils.ui.compose.SwitchPreferenceCompose @Composable fun DefaultOptionsSettingsScreen(modifier: Modifier = Modifier, viewModel: SettingsViewModel) { @@ -209,6 +211,16 @@ private fun Content( stepSize = SliderStepSizes.TRIGGER_SEQUENCE_TRIGGER_TIMEOUT, ) Spacer(Modifier.height(8.dp)) + + // Do not remap + SwitchPreferenceCompose( + title = stringResource(R.string.title_pref_default_do_not_remap), + text = stringResource(R.string.summary_pref_default_do_not_remap), + icon = Icons.Rounded.Keyboard, + isChecked = state.doNotRemap, + onCheckedChange = { callback.onDefaultDoNotRemapChanged(it) }, + ) + Spacer(Modifier.height(8.dp)) } } @@ -219,6 +231,7 @@ interface DefaultOptionsSettingsCallback { fun onRepeatDelayChanged(delay: Int) = run { } fun onRepeatRateChanged(rate: Int) = run { } fun onSequenceTriggerTimeoutChanged(timeout: Int) = run { } + fun onDefaultDoNotRemapChanged(doNotRemap: Boolean) = run { } } @Preview diff --git a/base/src/main/java/io/github/sds100/keymapper/base/settings/SettingsViewModel.kt b/base/src/main/java/io/github/sds100/keymapper/base/settings/SettingsViewModel.kt index 1a2ef5dbda..57dd804b14 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/settings/SettingsViewModel.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/settings/SettingsViewModel.kt @@ -72,27 +72,32 @@ class SettingsViewModel @Inject constructor( }.stateIn(viewModelScope, SharingStarted.Lazily, MainSettingsState()) val defaultSettingsScreenState: StateFlow = combine( - useCase.getPreference(Keys.defaultLongPressDelay), - useCase.getPreference(Keys.defaultDoublePressDelay), - useCase.getPreference(Keys.defaultVibrateDuration), - useCase.getPreference(Keys.defaultRepeatDelay), - useCase.getPreference(Keys.defaultRepeatRate), - useCase.getPreference(Keys.defaultSequenceTriggerTimeout), - ) { values -> - DefaultSettingsState( - longPressDelay = values[0] ?: PreferenceDefaults.LONG_PRESS_DELAY, - defaultLongPressDelay = PreferenceDefaults.LONG_PRESS_DELAY, - doublePressDelay = values[1] ?: PreferenceDefaults.DOUBLE_PRESS_DELAY, - defaultDoublePressDelay = PreferenceDefaults.DOUBLE_PRESS_DELAY, - vibrateDuration = values[2] ?: PreferenceDefaults.VIBRATION_DURATION, - defaultVibrateDuration = PreferenceDefaults.VIBRATION_DURATION, - repeatDelay = values[3] ?: PreferenceDefaults.REPEAT_DELAY, - defaultRepeatDelay = PreferenceDefaults.REPEAT_DELAY, - repeatRate = values[4] ?: PreferenceDefaults.REPEAT_RATE, - defaultRepeatRate = PreferenceDefaults.REPEAT_RATE, - sequenceTriggerTimeout = values[5] ?: PreferenceDefaults.SEQUENCE_TRIGGER_TIMEOUT, - defaultSequenceTriggerTimeout = PreferenceDefaults.SEQUENCE_TRIGGER_TIMEOUT, - ) + combine( + useCase.getPreference(Keys.defaultLongPressDelay), + useCase.getPreference(Keys.defaultDoublePressDelay), + useCase.getPreference(Keys.defaultVibrateDuration), + useCase.getPreference(Keys.defaultRepeatDelay), + useCase.getPreference(Keys.defaultRepeatRate), + useCase.getPreference(Keys.defaultSequenceTriggerTimeout), + ) { values -> + DefaultSettingsState( + longPressDelay = values[0] ?: PreferenceDefaults.LONG_PRESS_DELAY, + defaultLongPressDelay = PreferenceDefaults.LONG_PRESS_DELAY, + doublePressDelay = values[1] ?: PreferenceDefaults.DOUBLE_PRESS_DELAY, + defaultDoublePressDelay = PreferenceDefaults.DOUBLE_PRESS_DELAY, + vibrateDuration = values[2] ?: PreferenceDefaults.VIBRATION_DURATION, + defaultVibrateDuration = PreferenceDefaults.VIBRATION_DURATION, + repeatDelay = values[3] ?: PreferenceDefaults.REPEAT_DELAY, + defaultRepeatDelay = PreferenceDefaults.REPEAT_DELAY, + repeatRate = values[4] ?: PreferenceDefaults.REPEAT_RATE, + defaultRepeatRate = PreferenceDefaults.REPEAT_RATE, + sequenceTriggerTimeout = values[5] ?: PreferenceDefaults.SEQUENCE_TRIGGER_TIMEOUT, + defaultSequenceTriggerTimeout = PreferenceDefaults.SEQUENCE_TRIGGER_TIMEOUT, + ) + }, + useCase.getPreference(Keys.defaultDoNotRemap), + ) { state, doNotRemap -> + state.copy(doNotRemap = doNotRemap ?: PreferenceDefaults.DO_NOT_REMAP) }.stateIn(viewModelScope, SharingStarted.Lazily, DefaultSettingsState()) val automaticChangeImeSettingsState: StateFlow = combine( @@ -241,6 +246,12 @@ class SettingsViewModel @Inject constructor( } } + override fun onDefaultDoNotRemapChanged(doNotRemap: Boolean) { + viewModelScope.launch { + useCase.setPreference(Keys.defaultDoNotRemap, doNotRemap) + } + } + fun onShowToastWhenAutoChangingImeToggled(enabled: Boolean) { viewModelScope.launch { useCase.setPreference(Keys.showToastWhenAutoChangingIme, enabled) @@ -387,6 +398,8 @@ data class DefaultSettingsState( val sequenceTriggerTimeout: Int = PreferenceDefaults.SEQUENCE_TRIGGER_TIMEOUT, val defaultSequenceTriggerTimeout: Int = PreferenceDefaults.SEQUENCE_TRIGGER_TIMEOUT, + + val doNotRemap: Boolean = PreferenceDefaults.DO_NOT_REMAP, ) data class AutomaticChangeImeSettingsState( diff --git a/base/src/main/java/io/github/sds100/keymapper/base/trigger/ConfigTriggerDelegate.kt b/base/src/main/java/io/github/sds100/keymapper/base/trigger/ConfigTriggerDelegate.kt index 65c30dfc77..3a6d6265fb 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/trigger/ConfigTriggerDelegate.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/trigger/ConfigTriggerDelegate.kt @@ -66,6 +66,7 @@ class ConfigTriggerDelegate { device: KeyEventTriggerDevice, requiresIme: Boolean, otherTriggerKeys: List = emptyList(), + defaultDoNotRemap: Boolean = false, ): Trigger { val isPowerKey = KeyEventUtils.isPowerButtonKey(keyCode, scanCode) @@ -79,7 +80,7 @@ class ConfigTriggerDelegate { } } - var consumeKeyEvent = true + var consumeKeyEvent = !defaultDoNotRemap // Issue #753 if (KeyEventUtils.isModifierKey(keyCode)) { @@ -130,6 +131,7 @@ class ConfigTriggerDelegate { scanCode: Int, device: EvdevDeviceInfo, otherTriggerKeys: List = emptyList(), + defaultDoNotRemap: Boolean = false, ): Trigger { val isPowerKey = KeyEventUtils.isPowerButtonKey(keyCode, scanCode) @@ -158,7 +160,7 @@ class ConfigTriggerDelegate { scanCode = scanCode, device = device, clickType = clickType, - consumeEvent = true, + consumeEvent = !defaultDoNotRemap, detectWithScanCodeUserSetting = conflictingKeys.isNotEmpty(), ) diff --git a/base/src/main/java/io/github/sds100/keymapper/base/trigger/ConfigTriggerUseCase.kt b/base/src/main/java/io/github/sds100/keymapper/base/trigger/ConfigTriggerUseCase.kt index 9c6a10005e..e24483c034 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/trigger/ConfigTriggerUseCase.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/trigger/ConfigTriggerUseCase.kt @@ -118,6 +118,7 @@ class ConfigTriggerUseCaseImpl @Inject constructor( device, requiresIme, otherTriggerKeys = otherTriggerKeys, + defaultDoNotRemap = defaultDoNotRemap.value, ) } @@ -129,6 +130,7 @@ class ConfigTriggerUseCaseImpl @Inject constructor( scanCode, device, otherTriggerKeys = otherTriggerKeys, + defaultDoNotRemap = defaultDoNotRemap.value, ) } diff --git a/base/src/main/res/values/strings.xml b/base/src/main/res/values/strings.xml index bc808ec65f..c4d5de8e33 100644 --- a/base/src/main/res/values/strings.xml +++ b/base/src/main/res/values/strings.xml @@ -674,6 +674,9 @@ Change default options For triggers and actions + Do not remap by default + New trigger keys will not consume the key event by default + Reset all DANGER! Are you sure you want to reset all settings in the app to the default? Your existing key maps will not be affected. The introductions and warning pop ups will show again. diff --git a/data/src/main/java/io/github/sds100/keymapper/data/Keys.kt b/data/src/main/java/io/github/sds100/keymapper/data/Keys.kt index 59e88eb0a4..2e0ec15787 100644 --- a/data/src/main/java/io/github/sds100/keymapper/data/Keys.kt +++ b/data/src/main/java/io/github/sds100/keymapper/data/Keys.kt @@ -40,6 +40,7 @@ object Keys { val defaultRepeatRate = intPreferencesKey("pref_repeat_delay") val defaultSequenceTriggerTimeout = intPreferencesKey("pref_sequence_trigger_timeout") val defaultHoldDownDuration = intPreferencesKey("pref_hold_down_duration") + val defaultDoNotRemap = booleanPreferencesKey("pref_default_do_not_remap") val toggleKeyboardOnToggleKeymaps = booleanPreferencesKey("key_toggle_keyboard_on_pause_resume_keymaps") val automaticBackupLocation = stringPreferencesKey("pref_automatic_backup_location") diff --git a/data/src/main/java/io/github/sds100/keymapper/data/PreferenceDefaults.kt b/data/src/main/java/io/github/sds100/keymapper/data/PreferenceDefaults.kt index f241647dd6..fe20a9229d 100644 --- a/data/src/main/java/io/github/sds100/keymapper/data/PreferenceDefaults.kt +++ b/data/src/main/java/io/github/sds100/keymapper/data/PreferenceDefaults.kt @@ -22,4 +22,6 @@ object PreferenceDefaults { // It is false by default and the first time they turn on the system bridge, // the preference will be set to true. const val KEY_EVENT_ACTIONS_USE_SYSTEM_BRIDGE = false + + const val DO_NOT_REMAP = false }