Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
72 commits
Select commit Hold shift + click to select a range
4e0e707
Initial plan
Copilot Oct 31, 2025
841feaa
Add core implementation for modify system settings actions
Copilot Oct 31, 2025
d0d21d3
Add UI bottom sheet for modifying system settings
Copilot Oct 31, 2025
4cc6ed9
Add action title and description display for modify settings
Copilot Oct 31, 2025
6237188
Fix SystemBridge settings implementation with proper ContentResolver …
Copilot Oct 31, 2025
927a3d6
Refactor to use single ActionId with SettingType enum
Copilot Oct 31, 2025
51b18eb
Refactor settings modification to use SettingsAdapter and permissions
Copilot Oct 31, 2025
b57cdf8
Add ChooseSettingScreen and ViewModel for setting selection
Copilot Oct 31, 2025
c1f999f
Fix code review feedback: remove fully qualified names and use KeyMap…
Copilot Oct 31, 2025
c351c61
Refactor SettingsAdapter and ModifySettingActionBottomSheet per review
Copilot Oct 31, 2025
67d4efd
Merge branch 'develop' into copilot/modify-system-settings-permission
sds100 Nov 1, 2025
b149abf
Consolidate SettingsAdapter methods and import SettingType in ActionE…
Copilot Nov 2, 2025
0ac7e72
Initial plan
Copilot Nov 2, 2025
ecf4b6c
Add CREATE_NOTIFICATION action with basic implementation
Copilot Nov 2, 2025
4c3d41f
Add UI configuration screen for CREATE_NOTIFICATION action
Copilot Nov 2, 2025
1af1451
Address code review feedback
Copilot Nov 2, 2025
e182815
Fix validation and ID generation issues
Copilot Nov 2, 2025
3f26997
Initial plan
Copilot Nov 2, 2025
7c17425
Initial plan
Copilot Nov 2, 2025
10497b7
Add keyboard showing constraints UI components
Copilot Nov 2, 2025
5d8d3e8
Add keyboard constraint tests and UI helper
Copilot Nov 2, 2025
ea6d500
Improve keyboard constraint descriptions
Copilot Nov 2, 2025
0c9b394
Add toggle hotspot action boilerplate
Copilot Nov 2, 2025
2dd5f5f
Fix hotspot tethering implementation logic and method signature
Copilot Nov 2, 2025
e29329c
Address code review feedback - add TETHERING_WIFI constant and improv…
Copilot Nov 2, 2025
c8dcc53
Address PR review feedback
Copilot Nov 4, 2025
e441a04
Merge branch 'develop' into copilot/modify-system-settings-permission
sds100 Nov 7, 2025
162a959
#1871 clean up modify settings bottom sheet
sds100 Nov 7, 2025
16ef439
#1871 use segmented buttons to switch setting type
sds100 Nov 7, 2025
1862d3b
#1871 clean up ChooseSettingScreen.kt and add previews
sds100 Nov 8, 2025
a75ea9f
#1871 ModifySetting action is now editable
sds100 Nov 8, 2025
8149230
#1871 add backhandler to ChooseSettingScreen
sds100 Nov 8, 2025
95d22d0
#1871 fix dismissing ModifySettingActionBottomSheet
sds100 Nov 8, 2025
2b0efdc
chore: upgrade compose BOM and navigation libraries
sds100 Nov 8, 2025
0f168ad
#1871 use monospace font in ChooseSettingScreen.kt
sds100 Nov 8, 2025
94b77f2
#1871 fix saving ModifySettings action
sds100 Nov 8, 2025
f300bf8
#1871 add TODO
sds100 Nov 8, 2025
192868c
chore: bump version to 4.0.0-beta.3
sds100 Nov 8, 2025
fefc970
fix: do not crash when deserializing an unknown action type
sds100 Nov 9, 2025
04328e3
change logging message
sds100 Nov 9, 2025
9a92925
Merge branch 'develop' into copilot/modify-system-settings-permission
sds100 Nov 9, 2025
302095d
feat: WRITE_SECURE_SETTINGS permission dialog now directs the user to…
sds100 Nov 9, 2025
ca8951d
#1871 feat: complete modify system setting action
sds100 Nov 9, 2025
7475f2c
Merge pull request #1872 from keymapperorg/copilot/modify-system-sett…
sds100 Nov 9, 2025
ec764dd
style: reformat
sds100 Nov 9, 2025
91a73c5
Merge branch 'develop' into copilot/create-notification-channel
sds100 Nov 9, 2025
3f92503
#1221 feat: create action to show a notification
sds100 Nov 9, 2025
c0ac7a4
fix: save and restore state when configuring key maps
sds100 Nov 9, 2025
52d1d87
Merge branch 'copilot/create-notification-channel' into develop
sds100 Nov 9, 2025
9df0565
#1221 update changelog and whats-new
sds100 Nov 9, 2025
53bde3f
Merge branch 'develop' into copilot/add-toggle-hotspot-action
sds100 Nov 9, 2025
82316a8
fix: Do not automatically go back to Key Mapper after enabling wirele…
sds100 Nov 9, 2025
051ecec
feat: auto restart system bridge on debug builds
sds100 Nov 9, 2025
9c4910a
#1491 feat: action to toggle/enable/disable hotspot
sds100 Nov 9, 2025
5e2d207
Merge branch 'copilot/add-toggle-hotspot-action' into develop
sds100 Nov 9, 2025
c13d147
fix tests
sds100 Nov 9, 2025
bb4028d
fix: do not obfuscate SettingType enum
sds100 Nov 11, 2025
606fce1
Merge branch 'develop' into copilot/add-key-map-constraint
sds100 Nov 15, 2025
2474a5a
#1414 feat: constraint for when the keyboard is showing
sds100 Nov 15, 2025
1196d0c
Merge branch 'copilot/add-key-map-constraint' into develop
sds100 Nov 15, 2025
b7392fe
style: import rather than fully qualified name
sds100 Nov 18, 2025
f8b1ba8
fix: add serializable to evdev trigger key
sds100 Nov 18, 2025
4cae358
#1900 log to logcat if extra logging is enabled.
sds100 Nov 18, 2025
7e13471
#1900 log to logcat if extra logging is enabled.
sds100 Nov 18, 2025
511e61a
chore: bump version code
sds100 Nov 20, 2025
e9587d9
store: update feature graphic
sds100 Nov 20, 2025
8ab3f6f
#1901 prompt user to set default USB configuration to 'No data transf…
sds100 Nov 20, 2025
57b9edb
#1898 do not launch directly into the Wireless Debugging activity on …
sds100 Nov 20, 2025
8b4271a
#1902 feat: add toggle next to record trigger button to use PRO mode
sds100 Nov 20, 2025
bd70a55
chore: bump version code
sds100 Nov 20, 2025
76943a7
fix: do not show usb mode warning on rooted system bridge
sds100 Nov 20, 2025
4e1aea5
chore: update changelog and whats new for 4.0 beta 3
sds100 Nov 25, 2025
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
17 changes: 17 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,20 @@
## [4.0.0 Beta 3](https://github.com/sds100/KeyMapper/releases/tag/v4.0.0-beta.03)

#### 25 November 2025

## Added
- #1871 action to modify any system settings.
- #1221 action to show a custom notification.
- #1491 action to toggle/enable/disable hotspot.
- #1414 constraint for when the keyboard is showing.
- #1900 log to logcat if extra logging is enabled.
- #1902 add toggle next to record trigger button to use PRO mode.

## Bug fixes

- #1901 prompt user to set default USB configuration to 'No data transfer' after starting pro mode.
- #1898 do not launch directly into the Wireless Debugging activity on Xiaomi devices due to a bug they introduced.

## [4.0.0 Beta 2](https://github.com/sds100/KeyMapper/releases/tag/v4.0.0-beta.02)

#### 08 November 2025
Expand Down
5 changes: 5 additions & 0 deletions app/proguard-rules.pro
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,11 @@
-keep class io.github.sds100.keymapper.api.IKeyEventRelayServiceCallback$Stub { *; }
-keep class com.android.internal.telephony.ITelephony { *; }
-keep class com.android.internal.telephony.ITelephony$Stub { *; }
-keep class android.net.ITetheringConnector { *; }
-keep class android.net.ITetheringConnector$Stub { *; }
-keep class android.hardware.usb.IUsbManager { *; }
-keep class android.hardware.usb.IUsbManager$Stub { *; }
-keep class android.net.* { *; }

-keepattributes *Annotation*, InnerClasses
-dontnote kotlinx.serialization.AnnotationsKt # core serialization annotations
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import io.github.sds100.keymapper.base.trigger.TriggerSetupShortcut
import io.github.sds100.keymapper.base.utils.navigation.NavigationProvider
import io.github.sds100.keymapper.base.utils.ui.DialogProvider
import io.github.sds100.keymapper.base.utils.ui.ResourceProvider
import io.github.sds100.keymapper.sysbridge.manager.SystemBridgeConnectionManager
import javax.inject.Inject
import kotlinx.coroutines.launch

Expand All @@ -27,6 +28,7 @@ class ConfigTriggerViewModel @Inject constructor(
private val createKeyMapShortcut: CreateKeyMapShortcutUseCase,
private val displayKeyMap: DisplayKeyMapUseCase,
private val fingerprintGesturesSupported: FingerprintGesturesSupportedUseCase,
private val systemBridgeConnectionManager: SystemBridgeConnectionManager,
setupAccessibilityServiceDelegate: SetupAccessibilityServiceDelegate,
onboardingTipDelegate: OnboardingTipDelegate,
triggerSetupDelegate: TriggerSetupDelegate,
Expand All @@ -41,6 +43,7 @@ class ConfigTriggerViewModel @Inject constructor(
displayKeyMap = displayKeyMap,
fingerprintGesturesSupported = fingerprintGesturesSupported,
setupAccessibilityServiceDelegate = setupAccessibilityServiceDelegate,
systemBridgeConnectionManager = systemBridgeConnectionManager,
onboardingTipDelegate = onboardingTipDelegate,
triggerSetupDelegate = triggerSetupDelegate,
resourceProvider = resourceProvider,
Expand Down
4 changes: 2 additions & 2 deletions app/version.properties
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
VERSION_NAME=4.0.0-beta.2
VERSION_CODE=187
VERSION_NAME=4.0.0-beta.3
VERSION_CODE=194
VERSION_NUM=01
5 changes: 5 additions & 0 deletions base/src/main/assets/whats-new.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,17 @@ You can now remap ALL buttons when the screen is off (including the power button
• Send SMS messages
• Force stop current app or clear from recents
• Mute/unmute microphone
• Modify any system setting
• Show a custom notification
• Toggle hotspot

🆕 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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,17 @@ import androidx.lifecycle.Lifecycle
import androidx.lifecycle.flowWithLifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.withStateAtLeast
import androidx.navigation.findNavController
import com.anggrayudi.storage.extension.openInputStream
import com.anggrayudi.storage.extension.openOutputStream
import com.anggrayudi.storage.extension.toDocumentFile
import io.github.sds100.keymapper.base.compose.ComposeColors
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.system.accessibility.AccessibilityServiceAdapterImpl
import io.github.sds100.keymapper.base.system.permissions.RequestPermissionDelegate
import io.github.sds100.keymapper.base.trigger.RecordTriggerControllerImpl
import io.github.sds100.keymapper.base.utils.navigation.NavigationProvider
import io.github.sds100.keymapper.base.utils.ui.ResourceProviderImpl
import io.github.sds100.keymapper.common.BuildConfigProvider
import io.github.sds100.keymapper.sysbridge.service.SystemBridgeSetupControllerImpl
Expand All @@ -56,9 +56,6 @@ abstract class BaseMainActivity : AppCompatActivity() {
const val ACTION_SHOW_ACCESSIBILITY_SETTINGS_NOT_FOUND_DIALOG =
"${BuildConfig.LIBRARY_PACKAGE_NAME}.ACTION_SHOW_ACCESSIBILITY_SETTINGS_NOT_FOUND_DIALOG"

const val ACTION_USE_FLOATING_BUTTONS =
"${BuildConfig.LIBRARY_PACKAGE_NAME}.ACTION_USE_FLOATING_BUTTONS"

const val ACTION_SAVE_FILE = "${BuildConfig.LIBRARY_PACKAGE_NAME}.ACTION_SAVE_FILE"
const val EXTRA_FILE_URI = "${BuildConfig.LIBRARY_PACKAGE_NAME}.EXTRA_FILE_URI"

Expand All @@ -78,9 +75,6 @@ abstract class BaseMainActivity : AppCompatActivity() {
@Inject
lateinit var onboardingUseCase: OnboardingUseCase

@Inject
lateinit var recordTriggerController: RecordTriggerControllerImpl

@Inject
lateinit var notificationReceiverAdapter: NotificationReceiverAdapterImpl

Expand All @@ -105,6 +99,12 @@ abstract class BaseMainActivity : AppCompatActivity() {
@Inject
lateinit var inputEventHub: InputEventHubImpl

@Inject
lateinit var navigationProvider: NavigationProvider

@Inject
lateinit var configKeyMapState: ConfigKeyMapStateImpl

private lateinit var requestPermissionDelegate: RequestPermissionDelegate

private val currentNightMode: Int
Expand Down Expand Up @@ -155,22 +155,23 @@ abstract class BaseMainActivity : AppCompatActivity() {
)
super.onCreate(savedInstanceState)

savedInstanceState?.let { configKeyMapState.restoreState(it) }

requestPermissionDelegate = RequestPermissionDelegate(
this,
showDialogs = true,
permissionAdapter,
notificationReceiverAdapter = notificationReceiverAdapter,
buildConfigProvider = buildConfigProvider,
shizukuAdapter = shizukuAdapter,
navigationProvider = navigationProvider,
coroutineScope = lifecycleScope,
)

permissionAdapter.request
.flowWithLifecycle(lifecycle, Lifecycle.State.STARTED)
.onEach { permission ->
requestPermissionDelegate.requestPermission(
permission,
findNavController(R.id.container),
)
requestPermissionDelegate.requestPermission(permission)
}
.launchIn(lifecycleScope)

Expand Down Expand Up @@ -207,6 +208,12 @@ abstract class BaseMainActivity : AppCompatActivity() {
onboardingUseCase.handledMigrateScreenOffKeyMapsNotification()
}

override fun onSaveInstanceState(outState: Bundle) {
configKeyMapState.saveState(outState)

super.onSaveInstanceState(outState)
}

override fun onDestroy() {
onboardingUseCase.shownAppIntro = true

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import io.github.sds100.keymapper.base.actions.ChooseActionScreen
import io.github.sds100.keymapper.base.actions.ChooseActionViewModel
import io.github.sds100.keymapper.base.actions.ChooseSettingScreen
import io.github.sds100.keymapper.base.actions.ConfigShellCommandViewModel
import io.github.sds100.keymapper.base.actions.ShellCommandActionScreen
import io.github.sds100.keymapper.base.actions.uielement.InteractUiElementScreen
Expand Down Expand Up @@ -164,6 +165,13 @@ fun BaseMainNavHost(
)
}

composable<NavDestination.ChooseSetting> {
ChooseSettingScreen(
modifier = Modifier.fillMaxSize(),
viewModel = hiltViewModel(),
)
}

composableDestinations()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import io.github.sds100.keymapper.system.camera.CameraLens
import io.github.sds100.keymapper.system.intents.IntentExtraModel
import io.github.sds100.keymapper.system.intents.IntentTarget
import io.github.sds100.keymapper.system.network.HttpMethod
import io.github.sds100.keymapper.system.settings.SettingType
import io.github.sds100.keymapper.system.volume.DndMode
import io.github.sds100.keymapper.system.volume.RingerMode
import io.github.sds100.keymapper.system.volume.VolumeStream
Expand Down Expand Up @@ -667,6 +668,24 @@ sealed class ActionData : Comparable<ActionData> {
}
}

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

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

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

@Serializable
sealed class Brightness : ActionData() {
@Serializable
Expand Down Expand Up @@ -871,6 +890,17 @@ sealed class ActionData : Comparable<ActionData> {
override val id: ActionId = ActionId.DISMISS_ALL_NOTIFICATIONS
}

@Serializable
data class CreateNotification(val title: String, val text: String, val timeoutMs: Long?) :
ActionData() {
override val id: ActionId = ActionId.CREATE_NOTIFICATION

override fun compareTo(other: ActionData) = when (other) {
is CreateNotification -> title.compareTo(other.title)
else -> super.compareTo(other)
}
}

@Serializable
data object AnswerCall : ActionData() {
override val id: ActionId = ActionId.ANSWER_PHONE_CALL
Expand Down Expand Up @@ -949,4 +979,24 @@ sealed class ActionData : Comparable<ActionData> {
data object ClearRecentApp : ActionData() {
override val id: ActionId = ActionId.CLEAR_RECENT_APP
}

@Serializable
data class ModifySetting(
val settingType: SettingType,
val settingKey: String,
val value: String,
) : ActionData() {
override val id: ActionId = ActionId.MODIFY_SETTING

override fun compareTo(other: ActionData) = when (other) {
is ModifySetting -> compareValuesBy(
this,
other,
{ it.settingType },
{ it.settingKey },
{ it.value },
)
else -> super.compareTo(other)
}
}
}
Loading