Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
9886cda
write whats-new
sds100 May 10, 2026
4030b0f
bump version to 4.1.1
sds100 May 10, 2026
0dfaf05
#2076 use any input device as the default for triggers
sds100 May 15, 2026
1273c74
update email templates
sds100 May 15, 2026
7e805ef
#1369 feat: improve TalkBack accessibility for reorderable list items
claude May 17, 2026
c2eed0a
#262 feat: add support for accessibility navigation gestures (TalkBack)
claude May 24, 2026
b27fbe4
#1369 reformat and move code
sds100 May 29, 2026
e1a2c85
#262 add comment
sds100 May 29, 2026
9fd3c9f
#262 refactor code
sds100 May 29, 2026
a04e738
Merge pull request #2141 from keymapperorg/claude/issue-1369-talkback…
sds100 May 29, 2026
3980578
#262 use custom dialog to select talkback gesture action
sds100 May 29, 2026
6a79767
Merge branch 'develop' into claude/issue-262-talkback-gestures
sds100 May 29, 2026
0f5436c
Merge pull request #2144 from keymapperorg/claude/issue-262-talkback-…
sds100 May 29, 2026
248907c
#2140 feat: add monochrome app icon for themed icon support on Androi…
claude May 31, 2026
f28691a
#2074 fix: scope item drag to handle only, restoring list scroll
claude May 31, 2026
b83e893
bump version to 4.2.0 and update changelog
sds100 Jun 1, 2026
bcd72c8
Merge branch 'develop' into claude/issue-2074-fix-scroll-drag
sds100 Jun 1, 2026
7a1e06d
Merge branch 'develop' into claude/issue-2140-monochrome-icon
sds100 Jun 1, 2026
8b536ca
Merge pull request #2149 from keymapperorg/claude/issue-2074-fix-scro…
sds100 Jun 1, 2026
d26633e
fix: Support email string formatting fix
jambl3r Jun 3, 2026
43c38b0
use correct monochrome foreground icon
sds100 Jun 3, 2026
4b71c30
Merge branch 'develop' into claude/issue-2140-monochrome-icon
sds100 Jun 3, 2026
a02e99f
Merge pull request #2148 from keymapperorg/claude/issue-2140-monochro…
sds100 Jun 3, 2026
801b714
bump version code
sds100 Jun 3, 2026
7b260c4
add changelog release date
sds100 Jun 3, 2026
5197b3f
Merge pull request #2151 from keymapperorg/jambl3r-patch-1
sds100 Jun 3, 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
19 changes: 19 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,22 @@
## [4.2.0](https://github.com/sds100/KeyMapper/releases/tag/v4.2.0)

#### 03 June 2026

## Added

- #2140 Add monochrome app icon layer for themed icon support on Android 13+.

## Fixed

- #2074 Scrolling the action or trigger list no longer accidentally moves items; reordering by drag now only activates from the drag handle or via long-press.

## Changed

- #1369 Add content descriptions to drag handles and custom "Move up"/"Move down" accessibility actions for trigger and action list items, improving TalkBack support for reordering.
- #262 Add "TalkBack gesture" action to simulate TalkBack navigation gestures (swipes, multi-finger taps, and multi-directional swipes).
- #2076 Use "any input device" as the default for triggers.


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

#### 10 May 2026
Expand Down
1 change: 1 addition & 0 deletions app/src/ci/res/mipmap-anydpi-v26/ic_launcher.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
<monochrome android:drawable="@drawable/ic_launcher_monochrome"/>
</adaptive-icon>
1 change: 1 addition & 0 deletions app/src/ci/res/mipmap-anydpi-v26/ic_launcher_round.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
<monochrome android:drawable="@drawable/ic_launcher_monochrome"/>
</adaptive-icon>
1 change: 1 addition & 0 deletions app/src/debug/res/mipmap-anydpi-v26/ic_launcher.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
<monochrome android:drawable="@drawable/ic_launcher_monochrome"/>
</adaptive-icon>
1 change: 1 addition & 0 deletions app/src/debug/res/mipmap-anydpi-v26/ic_launcher_round.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
<monochrome android:drawable="@drawable/ic_launcher_monochrome"/>
</adaptive-icon>
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.1.0
VERSION_CODE=251
VERSION_NAME=4.2.0
VERSION_CODE=253
33 changes: 4 additions & 29 deletions base/src/main/assets/whats-new.txt
Original file line number Diff line number Diff line change
@@ -1,34 +1,9 @@
✨ Screen-off remapping
You can now remap ALL buttons when the screen is off (including the power button) for free with Expert mode.

🎯 New Actions
• Run shell commands
• Send SMS messages
• Force stop current app or clear from recents
• Mute/unmute microphone
• Modify any system setting
• Show a custom notification
• Toggle hotspot
• Toggle night shift

🆕 New Features
• Redesigned Settings screen
• Constraints for foldable hinge open/closed
• Shortcuts on the trigger screen to guide setup
• Select notification and alarm sounds for Sound action
• Constraints for keyboard is showing
• Switch next to record trigger button to use PRO mode


⚙️ Enhanced Controls
• Enable or disable all key maps in a group at once
• Intent API to enable, disable, or toggle individual key maps
• Floating buttons can now appear on top of keyboard or in notification panel
• Make floating buttons movable and completely invisible
• Select all text at the cursor
• Input on-screen keyboard enter/send button
• Show a toast message

🔧 Improvements
• Auto-switching keyboard more reliable and quicker on Android 13+
• Wi-Fi connected constraints more reliable
• A lot of bug fixes
• Bug fixes

📖 View the complete changelog at: http://keymapper.app/changelog
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.github.sds100.keymapper.base.actions

import io.github.sds100.keymapper.base.actions.talkback.TalkBackGestureType
import io.github.sds100.keymapper.common.models.ShellExecutionMode
import io.github.sds100.keymapper.common.utils.NodeInteractionType
import io.github.sds100.keymapper.common.utils.Orientation
Expand Down Expand Up @@ -95,6 +96,7 @@ sealed class ActionData : Comparable<ActionData> {
{ it.showVolumeUi },
{ it.volumeStream },
)

else -> super.compareTo(other)
}
}
Expand All @@ -111,6 +113,7 @@ sealed class ActionData : Comparable<ActionData> {
{ it.showVolumeUi },
{ it.volumeStream },
)

else -> super.compareTo(other)
}
}
Expand Down Expand Up @@ -1040,6 +1043,17 @@ sealed class ActionData : Comparable<ActionData> {
{ it.settingKey },
{ it.value },
)

else -> super.compareTo(other)
}
}

@Serializable
data class TalkBackGesture(val gesture: TalkBackGestureType) : ActionData() {
override val id: ActionId = ActionId.TALKBACK_GESTURE

override fun compareTo(other: ActionData) = when (other) {
is TalkBackGesture -> gesture.compareTo(other.gesture)
else -> super.compareTo(other)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package io.github.sds100.keymapper.base.actions

import android.util.Base64
import androidx.core.net.toUri
import io.github.sds100.keymapper.base.actions.talkback.TalkBackGestureType
import io.github.sds100.keymapper.common.models.ShellExecutionMode
import io.github.sds100.keymapper.common.utils.KMError
import io.github.sds100.keymapper.common.utils.KMResult
Expand Down Expand Up @@ -69,6 +70,7 @@ object ActionDataEntityMapper {
ActionEntity.Type.MODIFY_SETTING -> ActionId.MODIFY_SETTING

ActionEntity.Type.CREATE_NOTIFICATION -> ActionId.CREATE_NOTIFICATION

ActionEntity.Type.TOAST -> ActionId.TOAST
}

Expand Down Expand Up @@ -874,6 +876,20 @@ object ActionDataEntityMapper {

ActionId.CLEAR_RECENT_APP -> ActionData.ClearRecentApp

ActionId.TALKBACK_GESTURE -> {
val gestureTypeString =
entity.extras.getData(ActionEntity.EXTRA_TALKBACK_GESTURE_TYPE)
.valueOrNull() ?: return null

val gestureType = try {
TalkBackGestureType.valueOf(gestureTypeString)
} catch (_: IllegalArgumentException) {
return null
}

ActionData.TalkBackGesture(gesture = gestureType)
}

ActionId.MODIFY_SETTING -> {
val value = entity.extras.getData(ActionEntity.EXTRA_SETTING_VALUE)
.valueOrNull() ?: return null
Expand Down Expand Up @@ -1324,6 +1340,10 @@ object ActionDataEntityMapper {
EntityExtra(ActionEntity.EXTRA_TOAST_DURATION, data.duration.name),
)

is ActionData.TalkBackGesture -> listOf(
EntityExtra(ActionEntity.EXTRA_TALKBACK_GESTURE_TYPE, data.gesture.name),
)

else -> emptyList()
}

Expand Down Expand Up @@ -1510,5 +1530,7 @@ object ActionDataEntityMapper {
ActionId.CLEAR_RECENT_APP to "clear_recent_app",

ActionId.MODIFY_SETTING to "modify_setting",

ActionId.TALKBACK_GESTURE to "talkback_gesture",
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,10 @@ class LazyActionErrorSnapshot(
}
}

is ActionData.TalkBackGesture -> {
return getAppError(TALKBACK_PACKAGE_NAME)
}

else -> {}
}

Expand Down Expand Up @@ -317,3 +321,5 @@ interface ActionErrorSnapshot {
fun getError(action: ActionData): KMError?
fun getErrors(actions: List<ActionData>): Map<ActionData, KMError?>
}

private const val TALKBACK_PACKAGE_NAME = "com.google.android.marvin.talkback"
Original file line number Diff line number Diff line change
Expand Up @@ -161,4 +161,6 @@ enum class ActionId {
CLEAR_RECENT_APP,

MODIFY_SETTING,

TALKBACK_GESTURE,
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.CustomAccessibilityAction
import androidx.compose.ui.semantics.customActions
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
Expand All @@ -62,28 +65,45 @@ fun ActionListItem(
onRemoveClick: () -> Unit = {},
onFixClick: () -> Unit = {},
onTestClick: () -> Unit = {},
onMoveUp: (() -> Unit)? = null,
onMoveDown: (() -> Unit)? = null,
) {
val draggableState = rememberDraggableState {
dragDropState?.onDrag(Offset(0f, it))
}

val moveUpLabel = stringResource(R.string.accessibility_action_move_up)
val moveDownLabel = stringResource(R.string.accessibility_action_move_down)

Column(modifier = modifier.fillMaxWidth()) {
ElevatedCard(
modifier = Modifier
.fillMaxWidth()
.heightIn(min = 48.dp)
.height(IntrinsicSize.Min)
.padding(start = 16.dp, end = 16.dp)
.draggable(
state = draggableState,
enabled = isDraggingEnabled,
orientation = Orientation.Vertical,
startDragImmediately = false,
onDragStarted = { offset ->
dragDropState?.onDragStart(index, offset)
},
onDragStopped = { dragDropState?.onDragInterrupted() },
),
.semantics {
if (isReorderingEnabled) {
customActions = buildList {
onMoveUp?.let { action ->
add(
CustomAccessibilityAction(moveUpLabel) {
action()
true
},
)
}
onMoveDown?.let { action ->
add(
CustomAccessibilityAction(moveDownLabel) {
action()
true
},
)
}
}
}
},
colors = CardDefaults.elevatedCardColors(
containerColor = if (isDragging) {
MaterialTheme.colorScheme.surfaceContainerHighest
Expand All @@ -100,9 +120,20 @@ fun ActionListItem(

if (isReorderingEnabled) {
Icon(
modifier = Modifier.size(24.dp),
modifier = Modifier
.size(24.dp)
.draggable(
state = draggableState,
enabled = isDraggingEnabled,
orientation = Orientation.Vertical,
startDragImmediately = true,
onDragStarted = { offset ->
dragDropState?.onDragStart(index, offset)
},
onDragStopped = { dragDropState?.onDragInterrupted() },
),
imageVector = Icons.Rounded.DragHandle,
contentDescription = null,
contentDescription = stringResource(R.string.drag_handle_for, model.text),
tint = MaterialTheme.colorScheme.onSurface,
)
}
Expand Down
Loading
Loading