Skip to content

Commit 4eb4095

Browse files
authored
Merge pull request #1907 from keymapperorg/develop
4.0.0 Beta 3
2 parents 0848974 + 4e1aea5 commit 4eb4095

File tree

97 files changed

+2956
-223
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

97 files changed

+2956
-223
lines changed

CHANGELOG.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,20 @@
1+
## [4.0.0 Beta 3](https://github.com/sds100/KeyMapper/releases/tag/v4.0.0-beta.03)
2+
3+
#### 25 November 2025
4+
5+
## Added
6+
- #1871 action to modify any system settings.
7+
- #1221 action to show a custom notification.
8+
- #1491 action to toggle/enable/disable hotspot.
9+
- #1414 constraint for when the keyboard is showing.
10+
- #1900 log to logcat if extra logging is enabled.
11+
- #1902 add toggle next to record trigger button to use PRO mode.
12+
13+
## Bug fixes
14+
15+
- #1901 prompt user to set default USB configuration to 'No data transfer' after starting pro mode.
16+
- #1898 do not launch directly into the Wireless Debugging activity on Xiaomi devices due to a bug they introduced.
17+
118
## [4.0.0 Beta 2](https://github.com/sds100/KeyMapper/releases/tag/v4.0.0-beta.02)
219

320
#### 08 November 2025

app/proguard-rules.pro

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,11 @@
7777
-keep class io.github.sds100.keymapper.api.IKeyEventRelayServiceCallback$Stub { *; }
7878
-keep class com.android.internal.telephony.ITelephony { *; }
7979
-keep class com.android.internal.telephony.ITelephony$Stub { *; }
80+
-keep class android.net.ITetheringConnector { *; }
81+
-keep class android.net.ITetheringConnector$Stub { *; }
82+
-keep class android.hardware.usb.IUsbManager { *; }
83+
-keep class android.hardware.usb.IUsbManager$Stub { *; }
84+
-keep class android.net.* { *; }
8085

8186
-keepattributes *Annotation*, InnerClasses
8287
-dontnote kotlinx.serialization.AnnotationsKt # core serialization annotations

app/src/main/java/io/github/sds100/keymapper/trigger/ConfigTriggerViewModel.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import io.github.sds100.keymapper.base.trigger.TriggerSetupShortcut
1616
import io.github.sds100.keymapper.base.utils.navigation.NavigationProvider
1717
import io.github.sds100.keymapper.base.utils.ui.DialogProvider
1818
import io.github.sds100.keymapper.base.utils.ui.ResourceProvider
19+
import io.github.sds100.keymapper.sysbridge.manager.SystemBridgeConnectionManager
1920
import javax.inject.Inject
2021
import kotlinx.coroutines.launch
2122

@@ -27,6 +28,7 @@ class ConfigTriggerViewModel @Inject constructor(
2728
private val createKeyMapShortcut: CreateKeyMapShortcutUseCase,
2829
private val displayKeyMap: DisplayKeyMapUseCase,
2930
private val fingerprintGesturesSupported: FingerprintGesturesSupportedUseCase,
31+
private val systemBridgeConnectionManager: SystemBridgeConnectionManager,
3032
setupAccessibilityServiceDelegate: SetupAccessibilityServiceDelegate,
3133
onboardingTipDelegate: OnboardingTipDelegate,
3234
triggerSetupDelegate: TriggerSetupDelegate,
@@ -41,6 +43,7 @@ class ConfigTriggerViewModel @Inject constructor(
4143
displayKeyMap = displayKeyMap,
4244
fingerprintGesturesSupported = fingerprintGesturesSupported,
4345
setupAccessibilityServiceDelegate = setupAccessibilityServiceDelegate,
46+
systemBridgeConnectionManager = systemBridgeConnectionManager,
4447
onboardingTipDelegate = onboardingTipDelegate,
4548
triggerSetupDelegate = triggerSetupDelegate,
4649
resourceProvider = resourceProvider,

app/version.properties

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
VERSION_NAME=4.0.0-beta.2
2-
VERSION_CODE=187
1+
VERSION_NAME=4.0.0-beta.3
2+
VERSION_CODE=194
33
VERSION_NUM=01

base/src/main/assets/whats-new.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,17 @@ You can now remap ALL buttons when the screen is off (including the power button
66
• Send SMS messages
77
• Force stop current app or clear from recents
88
• Mute/unmute microphone
9+
• Modify any system setting
10+
• Show a custom notification
11+
• Toggle hotspot
912

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

1621
⚙️ Enhanced Controls
1722
• Enable or disable all key maps in a group at once

base/src/main/java/io/github/sds100/keymapper/base/BaseMainActivity.kt

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,17 @@ import androidx.lifecycle.Lifecycle
2121
import androidx.lifecycle.flowWithLifecycle
2222
import androidx.lifecycle.lifecycleScope
2323
import androidx.lifecycle.withStateAtLeast
24-
import androidx.navigation.findNavController
2524
import com.anggrayudi.storage.extension.openInputStream
2625
import com.anggrayudi.storage.extension.openOutputStream
2726
import com.anggrayudi.storage.extension.toDocumentFile
2827
import io.github.sds100.keymapper.base.compose.ComposeColors
2928
import io.github.sds100.keymapper.base.input.InputEventDetectionSource
3029
import io.github.sds100.keymapper.base.input.InputEventHubImpl
30+
import io.github.sds100.keymapper.base.keymaps.ConfigKeyMapStateImpl
3131
import io.github.sds100.keymapper.base.onboarding.OnboardingUseCase
3232
import io.github.sds100.keymapper.base.system.accessibility.AccessibilityServiceAdapterImpl
3333
import io.github.sds100.keymapper.base.system.permissions.RequestPermissionDelegate
34-
import io.github.sds100.keymapper.base.trigger.RecordTriggerControllerImpl
34+
import io.github.sds100.keymapper.base.utils.navigation.NavigationProvider
3535
import io.github.sds100.keymapper.base.utils.ui.ResourceProviderImpl
3636
import io.github.sds100.keymapper.common.BuildConfigProvider
3737
import io.github.sds100.keymapper.sysbridge.service.SystemBridgeSetupControllerImpl
@@ -56,9 +56,6 @@ abstract class BaseMainActivity : AppCompatActivity() {
5656
const val ACTION_SHOW_ACCESSIBILITY_SETTINGS_NOT_FOUND_DIALOG =
5757
"${BuildConfig.LIBRARY_PACKAGE_NAME}.ACTION_SHOW_ACCESSIBILITY_SETTINGS_NOT_FOUND_DIALOG"
5858

59-
const val ACTION_USE_FLOATING_BUTTONS =
60-
"${BuildConfig.LIBRARY_PACKAGE_NAME}.ACTION_USE_FLOATING_BUTTONS"
61-
6259
const val ACTION_SAVE_FILE = "${BuildConfig.LIBRARY_PACKAGE_NAME}.ACTION_SAVE_FILE"
6360
const val EXTRA_FILE_URI = "${BuildConfig.LIBRARY_PACKAGE_NAME}.EXTRA_FILE_URI"
6461

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

81-
@Inject
82-
lateinit var recordTriggerController: RecordTriggerControllerImpl
83-
8478
@Inject
8579
lateinit var notificationReceiverAdapter: NotificationReceiverAdapterImpl
8680

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

102+
@Inject
103+
lateinit var navigationProvider: NavigationProvider
104+
105+
@Inject
106+
lateinit var configKeyMapState: ConfigKeyMapStateImpl
107+
108108
private lateinit var requestPermissionDelegate: RequestPermissionDelegate
109109

110110
private val currentNightMode: Int
@@ -155,22 +155,23 @@ abstract class BaseMainActivity : AppCompatActivity() {
155155
)
156156
super.onCreate(savedInstanceState)
157157

158+
savedInstanceState?.let { configKeyMapState.restoreState(it) }
159+
158160
requestPermissionDelegate = RequestPermissionDelegate(
159161
this,
160162
showDialogs = true,
161163
permissionAdapter,
162164
notificationReceiverAdapter = notificationReceiverAdapter,
163165
buildConfigProvider = buildConfigProvider,
164166
shizukuAdapter = shizukuAdapter,
167+
navigationProvider = navigationProvider,
168+
coroutineScope = lifecycleScope,
165169
)
166170

167171
permissionAdapter.request
168172
.flowWithLifecycle(lifecycle, Lifecycle.State.STARTED)
169173
.onEach { permission ->
170-
requestPermissionDelegate.requestPermission(
171-
permission,
172-
findNavController(R.id.container),
173-
)
174+
requestPermissionDelegate.requestPermission(permission)
174175
}
175176
.launchIn(lifecycleScope)
176177

@@ -207,6 +208,12 @@ abstract class BaseMainActivity : AppCompatActivity() {
207208
onboardingUseCase.handledMigrateScreenOffKeyMapsNotification()
208209
}
209210

211+
override fun onSaveInstanceState(outState: Bundle) {
212+
configKeyMapState.saveState(outState)
213+
214+
super.onSaveInstanceState(outState)
215+
}
216+
210217
override fun onDestroy() {
211218
onboardingUseCase.shownAppIntro = true
212219

base/src/main/java/io/github/sds100/keymapper/base/BaseMainNavHost.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import androidx.navigation.compose.NavHost
1818
import androidx.navigation.compose.composable
1919
import io.github.sds100.keymapper.base.actions.ChooseActionScreen
2020
import io.github.sds100.keymapper.base.actions.ChooseActionViewModel
21+
import io.github.sds100.keymapper.base.actions.ChooseSettingScreen
2122
import io.github.sds100.keymapper.base.actions.ConfigShellCommandViewModel
2223
import io.github.sds100.keymapper.base.actions.ShellCommandActionScreen
2324
import io.github.sds100.keymapper.base.actions.uielement.InteractUiElementScreen
@@ -164,6 +165,13 @@ fun BaseMainNavHost(
164165
)
165166
}
166167

168+
composable<NavDestination.ChooseSetting> {
169+
ChooseSettingScreen(
170+
modifier = Modifier.fillMaxSize(),
171+
viewModel = hiltViewModel(),
172+
)
173+
}
174+
167175
composableDestinations()
168176
}
169177
}

base/src/main/java/io/github/sds100/keymapper/base/actions/ActionData.kt

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import io.github.sds100.keymapper.system.camera.CameraLens
88
import io.github.sds100.keymapper.system.intents.IntentExtraModel
99
import io.github.sds100.keymapper.system.intents.IntentTarget
1010
import io.github.sds100.keymapper.system.network.HttpMethod
11+
import io.github.sds100.keymapper.system.settings.SettingType
1112
import io.github.sds100.keymapper.system.volume.DndMode
1213
import io.github.sds100.keymapper.system.volume.RingerMode
1314
import io.github.sds100.keymapper.system.volume.VolumeStream
@@ -667,6 +668,24 @@ sealed class ActionData : Comparable<ActionData> {
667668
}
668669
}
669670

671+
@Serializable
672+
sealed class Hotspot : ActionData() {
673+
@Serializable
674+
data object Enable : Hotspot() {
675+
override val id = ActionId.ENABLE_HOTSPOT
676+
}
677+
678+
@Serializable
679+
data object Disable : Hotspot() {
680+
override val id = ActionId.DISABLE_HOTSPOT
681+
}
682+
683+
@Serializable
684+
data object Toggle : Hotspot() {
685+
override val id = ActionId.TOGGLE_HOTSPOT
686+
}
687+
}
688+
670689
@Serializable
671690
sealed class Brightness : ActionData() {
672691
@Serializable
@@ -871,6 +890,17 @@ sealed class ActionData : Comparable<ActionData> {
871890
override val id: ActionId = ActionId.DISMISS_ALL_NOTIFICATIONS
872891
}
873892

893+
@Serializable
894+
data class CreateNotification(val title: String, val text: String, val timeoutMs: Long?) :
895+
ActionData() {
896+
override val id: ActionId = ActionId.CREATE_NOTIFICATION
897+
898+
override fun compareTo(other: ActionData) = when (other) {
899+
is CreateNotification -> title.compareTo(other.title)
900+
else -> super.compareTo(other)
901+
}
902+
}
903+
874904
@Serializable
875905
data object AnswerCall : ActionData() {
876906
override val id: ActionId = ActionId.ANSWER_PHONE_CALL
@@ -949,4 +979,24 @@ sealed class ActionData : Comparable<ActionData> {
949979
data object ClearRecentApp : ActionData() {
950980
override val id: ActionId = ActionId.CLEAR_RECENT_APP
951981
}
982+
983+
@Serializable
984+
data class ModifySetting(
985+
val settingType: SettingType,
986+
val settingKey: String,
987+
val value: String,
988+
) : ActionData() {
989+
override val id: ActionId = ActionId.MODIFY_SETTING
990+
991+
override fun compareTo(other: ActionData) = when (other) {
992+
is ModifySetting -> compareValuesBy(
993+
this,
994+
other,
995+
{ it.settingType },
996+
{ it.settingKey },
997+
{ it.value },
998+
)
999+
else -> super.compareTo(other)
1000+
}
1001+
}
9521002
}

0 commit comments

Comments
 (0)