Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
f3595ee
#1539 fix: converted strings to en-US
jambl3r Jan 15, 2026
0783483
style: reformat
sds100 Jan 16, 2026
9728352
chore: bump version to 4.0.0 beta 7
sds100 Jan 16, 2026
1627725
#1970 fix: dynamically build the key code list so key codes in new An…
sds100 Jan 16, 2026
2e74b9d
Merge remote-tracking branch 'origin/develop' into develop
sds100 Jan 16, 2026
56c8286
fix: do not save system bridge time relative to boot
sds100 Jan 16, 2026
ec88303
auto start expert mode if it was manually started after the last auto…
sds100 Jan 16, 2026
0f04175
#1939 show a notification when expert mode fails to start due to bein…
sds100 Jan 16, 2026
4e63c1c
#1986 fix: trigger screen is usable on slightly rectangular screens w…
sds100 Jan 16, 2026
ca68a68
fix: refresh the system bridge starter script when the app launches i…
sds100 Jan 16, 2026
2eab0fc
#1972 fix: expert mode works on Android 10
sds100 Jan 16, 2026
03cb80c
#1976 fix: panic in Rust system bridge code when unwrapping /dev/inpu…
sds100 Jan 16, 2026
e85fa17
Update README.md to not exclude mouse buttons
jambl3r Jan 16, 2026
79f4fea
#1971 fix: media actions work again in some apps, like YouTube
sds100 Jan 16, 2026
e86b616
Merge remote-tracking branch 'origin/develop' into develop
sds100 Jan 16, 2026
0213f49
style: reformat
sds100 Jan 16, 2026
b0f86bf
cargo reformat
sds100 Jan 16, 2026
59f3369
#1984 do not show "use expert mode" button for tips if expert mode al…
sds100 Jan 16, 2026
09738d6
#1977 potentially reduce the amount of "Failed to discover ADB port" …
sds100 Jan 16, 2026
c8e85dc
style: reformat
sds100 Jan 16, 2026
b50092d
chore: upgrade android gradle plugin
sds100 Jan 16, 2026
8e1ee50
#1973 add option to change app language on Android 13+
sds100 Jan 17, 2026
9a10bf8
add issue links to changelog
sds100 Jan 17, 2026
d5f64a9
style: reformat
sds100 Jan 17, 2026
f31b2c3
style: reformat
sds100 Jan 17, 2026
bd08ee2
fix: corrected discord link in app listing
jambl3r Jan 17, 2026
ac6cf6d
make log less verbose
sds100 Jan 17, 2026
dc797cd
Merge remote-tracking branch 'origin/develop' into develop
sds100 Jan 17, 2026
e372c76
fix: NetworkAdapter now updates isWifiConnected correctly
sds100 Jan 17, 2026
4c61530
chore: upgrade foss gradle wrapper version
sds100 Jan 17, 2026
4a3b117
fix: do not momentarily show pair adb steps after connecting to a wif…
sds100 Jan 17, 2026
7a8b2f6
fix: move switch to end of SwitchText and make text fill space
sds100 Jan 17, 2026
afe5d70
make floating button config screen more compact
sds100 Jan 17, 2026
4eafe19
#1961 fix: disabling setup assistant shows a notification asking for …
sds100 Jan 17, 2026
a37c5bd
fix: network adapter does not check network state in a way that there…
sds100 Jan 17, 2026
9dd14a3
log unix timestamp in system bridge auto starter
sds100 Jan 18, 2026
fd3fcec
set adb command result log level to debug
sds100 Jan 18, 2026
367c3c9
log more reasons why system bridge auto starting fails
sds100 Jan 18, 2026
86dc23e
style: extract long lines into separate method
sds100 Jan 18, 2026
bf74cfe
#1983 fix: inputting a modifier key and another key as actions throug…
sds100 Jan 18, 2026
9c9dcaf
fix: choosing key mapper input method does not launch IME settings sc…
sds100 Jan 18, 2026
da0b7db
#1990 fix: Passthrough the device id of the trigger to the key event …
sds100 Jan 18, 2026
863b7c6
#1982 feat: text action does not need Key Mapper input method on Andr…
sds100 Jan 18, 2026
e21ccff
#1989 center the "Trigger and actions" and "Constraint and more" tabs
sds100 Jan 18, 2026
7d7ddc0
#1392 feat: add action to enable/disable/toggle night shift
sds100 Jan 18, 2026
9f4aa6c
#1675 feat: option to make floating buttons movable and not locked in…
sds100 Jan 18, 2026
fdad2d3
#1949 feat: floating buttons are completely invisible when pressed if…
sds100 Jan 18, 2026
1ccd872
fix tests
sds100 Jan 18, 2026
d059f18
#1949 invisible floating buttons are clickable
sds100 Jan 18, 2026
ed5552a
chore: bump version code
sds100 Jan 18, 2026
38cc2c7
chore: update whats new
sds100 Jan 18, 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
677 changes: 353 additions & 324 deletions CHANGELOG.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,13 @@ Key Mapper supports a huge variety of buttons and keys:
- ALL your phone buttons (volume AND side key)
- Game controllers (D-pad, ABXY, and most others)
- Keyboards
- Mouse buttons
- Headsets and headphones
- Fingerprint sensor

Most devices are already supported, with new devices being added over time. Let us know if it's not working for you and we can prioritize your device.

Not currently supported:
- Mouse buttons
- Joysticks and triggers (LT,RT) on gamepads

Not enough keys? Design your own on-screen button layouts and remap those just like real keys!
Expand Down
4 changes: 4 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,10 @@ android {
}
}

androidResources {
generateLocaleConfig = true
}

buildFeatures {
dataBinding = true
aidl = true
Expand Down
1 change: 1 addition & 0 deletions app/src/main/res/resources.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
unqualifiedResLocale=en-US
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.0-beta.06
VERSION_CODE=226
VERSION_NAME=4.0.0-beta.07
VERSION_CODE=237
11 changes: 10 additions & 1 deletion base/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

<uses-permission android:name="android.permission.USE_FINGERPRINT" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.READ_LOGS"/>
<uses-permission android:name="android.permission.READ_LOGS" />

<uses-permission
android:name="android.permission.QUERY_ALL_PACKAGES"
Expand Down Expand Up @@ -117,5 +117,14 @@
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths" />
</provider>

<service
android:name="androidx.appcompat.app.AppLocalesMetadataHolderService"
android:enabled="false"
android:exported="false">
<meta-data
android:name="autoStoreLocales"
android:value="true" />
</service>
</application>
</manifest>
6 changes: 4 additions & 2 deletions base/src/main/assets/whats-new.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ You can now remap ALL buttons when the screen is off (including the power button
• Select notification and alarm sounds for Sound action
• Constraints for keyboard is showing
• Switch next to record trigger button to use PRO mode
• Make floating buttons movable and completely invisible
• Action to toggle night shift

⚙️ Enhanced Controls
• Enable or disable all key maps in a group at once
Expand All @@ -26,6 +28,6 @@ You can now remap ALL buttons when the screen is off (including the power button
🔧 Improvements
• Auto-switching keyboard more reliable and quicker on Android 13+
• Wi-Fi connected constraints more reliable
Various bug fixes and performance optimizations
A lot of bug fixes

📖 View the complete changelog at: http://changelog.keymapper.club
📖 View the complete changelog at: http://keymapper.app/changelog
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import io.github.sds100.keymapper.base.input.InputEventDetectionSource
import io.github.sds100.keymapper.base.input.InputEventHubImpl
import io.github.sds100.keymapper.base.keymaps.ConfigKeyMapStateImpl
import io.github.sds100.keymapper.base.onboarding.OnboardingUseCase
import io.github.sds100.keymapper.base.settings.AppLocaleAdapterImpl
import io.github.sds100.keymapper.base.system.accessibility.AccessibilityServiceAdapterImpl
import io.github.sds100.keymapper.base.system.permissions.RequestPermissionDelegate
import io.github.sds100.keymapper.base.utils.navigation.NavigationProvider
Expand Down Expand Up @@ -104,6 +105,9 @@ abstract class BaseMainActivity : AppCompatActivity() {
@Inject
lateinit var configKeyMapState: ConfigKeyMapStateImpl

@Inject
lateinit var appLocaleAdapter: AppLocaleAdapterImpl

private lateinit var requestPermissionDelegate: RequestPermissionDelegate

private val currentNightMode: Int
Expand Down Expand Up @@ -201,6 +205,7 @@ abstract class BaseMainActivity : AppCompatActivity() {
systemBridgeSetupController.invalidateSettings()
networkAdapter.invalidateState()
onboardingUseCase.handledMigrateScreenOffKeyMapsNotification()
appLocaleAdapter.invalidate()
}

override fun onSaveInstanceState(outState: Bundle) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ import io.github.sds100.keymapper.base.onboarding.OnboardingUseCase
import io.github.sds100.keymapper.base.onboarding.OnboardingUseCaseImpl
import io.github.sds100.keymapper.base.onboarding.SetupAccessibilityServiceDelegate
import io.github.sds100.keymapper.base.onboarding.SetupAccessibilityServiceDelegateImpl
import io.github.sds100.keymapper.base.settings.AppLocaleAdapter
import io.github.sds100.keymapper.base.settings.AppLocaleAdapterImpl
import io.github.sds100.keymapper.base.system.accessibility.AccessibilityServiceAdapterImpl
import io.github.sds100.keymapper.base.system.accessibility.ControlAccessibilityServiceUseCase
import io.github.sds100.keymapper.base.system.accessibility.ControlAccessibilityServiceUseCaseImpl
Expand Down Expand Up @@ -208,4 +210,8 @@ abstract class BaseSingletonHiltModule {
@Binds
@Singleton
abstract fun bindClock(impl: ClockImpl): Clock

@Binds
@Singleton
abstract fun bindAppLocaleAdapter(impl: AppLocaleAdapterImpl): AppLocaleAdapter
}
Original file line number Diff line number Diff line change
Expand Up @@ -714,6 +714,24 @@ sealed class ActionData : Comparable<ActionData> {
}
}

@Serializable
sealed class NightShift : ActionData() {
@Serializable
data object Enable : NightShift() {
override val id = ActionId.ENABLE_NIGHT_SHIFT
}

@Serializable
data object Disable : NightShift() {
override val id = ActionId.DISABLE_NIGHT_SHIFT
}

@Serializable
data object Toggle : NightShift() {
override val id = ActionId.TOGGLE_NIGHT_SHIFT
}
}

@Serializable
sealed class StatusBar : ActionData() {
@Serializable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -498,6 +498,10 @@ object ActionDataEntityMapper {
ActionId.INCREASE_BRIGHTNESS -> ActionData.Brightness.Increase
ActionId.DECREASE_BRIGHTNESS -> ActionData.Brightness.Decrease

ActionId.TOGGLE_NIGHT_SHIFT -> ActionData.NightShift.Toggle
ActionId.ENABLE_NIGHT_SHIFT -> ActionData.NightShift.Enable
ActionId.DISABLE_NIGHT_SHIFT -> ActionData.NightShift.Disable

ActionId.TOGGLE_AUTO_ROTATE -> ActionData.Rotation.ToggleAuto
ActionId.ENABLE_AUTO_ROTATE -> ActionData.Rotation.EnableAuto
ActionId.DISABLE_AUTO_ROTATE -> ActionData.Rotation.DisableAuto
Expand Down Expand Up @@ -1238,6 +1242,10 @@ object ActionDataEntityMapper {
ActionId.INCREASE_BRIGHTNESS to "increase_brightness",
ActionId.DECREASE_BRIGHTNESS to "decrease_brightness",

ActionId.TOGGLE_NIGHT_SHIFT to "toggle_night_shift",
ActionId.ENABLE_NIGHT_SHIFT to "enable_night_shift",
ActionId.DISABLE_NIGHT_SHIFT to "disable_night_shift",

ActionId.TOGGLE_AUTO_ROTATE to "toggle_auto_rotate",
ActionId.ENABLE_AUTO_ROTATE to "enable_auto_rotate",
ActionId.DISABLE_AUTO_ROTATE to "disable_auto_rotate",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ enum class ActionId {
INCREASE_BRIGHTNESS,
DECREASE_BRIGHTNESS,

TOGGLE_NIGHT_SHIFT,
ENABLE_NIGHT_SHIFT,
DISABLE_NIGHT_SHIFT,

TOGGLE_AUTO_ROTATE,
ENABLE_AUTO_ROTATE,
DISABLE_AUTO_ROTATE,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -475,6 +475,10 @@ class ActionUiHelper(
ActionData.Brightness.Increase -> getString(R.string.action_increase_brightness)
ActionData.Brightness.ToggleAuto -> getString(R.string.action_toggle_auto_brightness)

ActionData.NightShift.Disable -> getString(R.string.action_disable_night_shift)
ActionData.NightShift.Enable -> getString(R.string.action_enable_night_shift)
ActionData.NightShift.Toggle -> getString(R.string.action_toggle_night_shift)

ActionData.ConsumeKeyEvent -> getString(R.string.action_consume_keyevent)

ActionData.ControlMedia.FastForward -> getString(R.string.action_fast_forward)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ import androidx.compose.material.icons.outlined.Mic
import androidx.compose.material.icons.outlined.MicOff
import androidx.compose.material.icons.outlined.MoreVert
import androidx.compose.material.icons.outlined.Nfc
import androidx.compose.material.icons.outlined.Nightlight
import androidx.compose.material.icons.outlined.NotStarted
import androidx.compose.material.icons.outlined.Pause
import androidx.compose.material.icons.outlined.PhonelinkRing
Expand Down Expand Up @@ -148,6 +149,9 @@ object ActionUtils {
ActionId.ENABLE_AUTO_BRIGHTNESS -> ActionCategory.DISPLAY
ActionId.INCREASE_BRIGHTNESS -> ActionCategory.DISPLAY
ActionId.DECREASE_BRIGHTNESS -> ActionCategory.DISPLAY
ActionId.TOGGLE_NIGHT_SHIFT -> ActionCategory.DISPLAY
ActionId.ENABLE_NIGHT_SHIFT -> ActionCategory.DISPLAY
ActionId.DISABLE_NIGHT_SHIFT -> ActionCategory.DISPLAY
ActionId.SCREENSHOT -> ActionCategory.DISPLAY
ActionId.TOGGLE_AUTO_ROTATE -> ActionCategory.INTERFACE
ActionId.ENABLE_AUTO_ROTATE -> ActionCategory.INTERFACE
Expand Down Expand Up @@ -275,6 +279,12 @@ object ActionUtils {

ActionId.DECREASE_BRIGHTNESS -> R.string.action_decrease_brightness

ActionId.TOGGLE_NIGHT_SHIFT -> R.string.action_toggle_night_shift

ActionId.ENABLE_NIGHT_SHIFT -> R.string.action_enable_night_shift

ActionId.DISABLE_NIGHT_SHIFT -> R.string.action_disable_night_shift

ActionId.TOGGLE_AUTO_ROTATE -> R.string.action_toggle_auto_rotate

ActionId.ENABLE_AUTO_ROTATE -> R.string.action_enable_auto_rotate
Expand Down Expand Up @@ -681,6 +691,11 @@ object ActionUtils {
ActionId.DISABLE_HOTSPOT,
-> Build.VERSION_CODES.R

ActionId.TOGGLE_NIGHT_SHIFT,
ActionId.ENABLE_NIGHT_SHIFT,
ActionId.DISABLE_NIGHT_SHIFT,
-> Build.VERSION_CODES.Q

else -> Constants.MIN_API
}

Expand Down Expand Up @@ -840,6 +855,11 @@ object ActionUtils {
ActionId.DECREASE_BRIGHTNESS,
-> return listOf(Permission.WRITE_SETTINGS)

ActionId.TOGGLE_NIGHT_SHIFT,
ActionId.ENABLE_NIGHT_SHIFT,
ActionId.DISABLE_NIGHT_SHIFT,
-> return listOf(Permission.WRITE_SECURE_SETTINGS)

ActionId.TOGGLE_FLASHLIGHT,
ActionId.ENABLE_FLASHLIGHT,
ActionId.DISABLE_FLASHLIGHT,
Expand Down Expand Up @@ -935,6 +955,9 @@ object ActionUtils {
ActionId.ENABLE_AUTO_BRIGHTNESS -> Icons.Outlined.BrightnessAuto
ActionId.INCREASE_BRIGHTNESS -> Icons.Outlined.BrightnessHigh
ActionId.DECREASE_BRIGHTNESS -> Icons.Outlined.BrightnessLow
ActionId.TOGGLE_NIGHT_SHIFT -> Icons.Outlined.Nightlight
ActionId.ENABLE_NIGHT_SHIFT -> Icons.Outlined.Nightlight
ActionId.DISABLE_NIGHT_SHIFT -> Icons.Outlined.Nightlight
ActionId.TOGGLE_AUTO_ROTATE -> Icons.Outlined.ScreenRotation
ActionId.ENABLE_AUTO_ROTATE -> Icons.Outlined.ScreenRotation
ActionId.DISABLE_AUTO_ROTATE -> Icons.Outlined.ScreenLockRotation
Expand Down Expand Up @@ -1060,7 +1083,7 @@ fun ActionData.canBeHeldDown(): Boolean = when (this) {

fun ActionData.canUseImeToPerform(): Boolean = when (this) {
is ActionData.InputKeyEvent -> true
is ActionData.Text -> true
is ActionData.Text -> Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU
else -> false
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -986,6 +986,12 @@ class CreateActionDelegate(

ActionId.DECREASE_BRIGHTNESS -> return ActionData.Brightness.Decrease

ActionId.TOGGLE_NIGHT_SHIFT -> return ActionData.NightShift.Toggle

ActionId.ENABLE_NIGHT_SHIFT -> return ActionData.NightShift.Enable

ActionId.DISABLE_NIGHT_SHIFT -> return ActionData.NightShift.Disable

ActionId.TOGGLE_AUTO_ROTATE -> return ActionData.Rotation.ToggleAuto

ActionId.ENABLE_AUTO_ROTATE -> return ActionData.Rotation.EnableAuto
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,10 @@ sealed class PerformActionTriggerDevice {
*/
data class Evdev(val deviceId: Int) : PerformActionTriggerDevice()

/**
* The action was triggered by an Android InputDevice.
*/
data class AndroidDevice(val deviceId: Int) : PerformActionTriggerDevice()

data object Default : PerformActionTriggerDevice()
}
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ import io.github.sds100.keymapper.system.phone.PhoneAdapter
import io.github.sds100.keymapper.system.popup.ToastAdapter
import io.github.sds100.keymapper.system.ringtones.RingtoneAdapter
import io.github.sds100.keymapper.system.root.SuAdapter
import io.github.sds100.keymapper.system.settings.SettingType
import io.github.sds100.keymapper.system.settings.SettingsAdapter
import io.github.sds100.keymapper.system.shell.ShellAdapter
import io.github.sds100.keymapper.system.url.OpenUrlAdapter
import io.github.sds100.keymapper.system.volume.RingerMode
Expand Down Expand Up @@ -118,9 +120,13 @@ class PerformActionsUseCaseImpl @AssistedInject constructor(
private val settingsRepository: PreferenceRepository,
private val inputEventHub: InputEventHub,
private val systemBridgeConnectionManager: SystemBridgeConnectionManager,
private val settingsAdapter: io.github.sds100.keymapper.system.settings.SettingsAdapter,
private val settingsAdapter: SettingsAdapter,
) : PerformActionsUseCase {

companion object {
private const val SETTING_NIGHT_DISPLAY_ACTIVATED = "night_display_activated"
}

@AssistedFactory
interface Factory {
fun create(
Expand Down Expand Up @@ -375,7 +381,11 @@ class PerformActionsUseCaseImpl @AssistedInject constructor(
}

is ActionData.Text -> {
keyMapperImeMessenger.inputText(action.text)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
service.injectText(action.text)
} else {
keyMapperImeMessenger.inputText(action.text)
}
result = Success(Unit)
}

Expand Down Expand Up @@ -459,11 +469,19 @@ class PerformActionsUseCaseImpl @AssistedInject constructor(
}

is ActionData.Hotspot.Enable -> {
result = networkAdapter.enableHotspot()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
result = networkAdapter.enableHotspot()
} else {
result = SdkVersionTooLow(minSdk = Build.VERSION_CODES.R)
}
}

is ActionData.Hotspot.Disable -> {
result = networkAdapter.disableHotspot()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
result = networkAdapter.disableHotspot()
} else {
result = SdkVersionTooLow(minSdk = Build.VERSION_CODES.R)
}
}

is ActionData.Brightness.ToggleAuto -> {
Expand Down Expand Up @@ -1039,6 +1057,35 @@ class PerformActionsUseCaseImpl @AssistedInject constructor(
action.value,
)
}

is ActionData.NightShift.Enable -> {
result = settingsAdapter.setValue(
SettingType.SECURE,
SETTING_NIGHT_DISPLAY_ACTIVATED,
"1",
)
}

is ActionData.NightShift.Disable -> {
result = settingsAdapter.setValue(
SettingType.SECURE,
SETTING_NIGHT_DISPLAY_ACTIVATED,
"0",
)
}

is ActionData.NightShift.Toggle -> {
val currentValue = settingsAdapter.getValue(
SettingType.SECURE,
SETTING_NIGHT_DISPLAY_ACTIVATED,
)
val newValue = if (currentValue == "1") "0" else "1"
result = settingsAdapter.setValue(
SettingType.SECURE,
SETTING_NIGHT_DISPLAY_ACTIVATED,
newValue,
)
}
}

when (result) {
Expand Down
Loading
Loading