Skip to content

Commit ef6ac48

Browse files
authored
Merge pull request #1793 from keymapperorg/feature/1394-kernel-remapping-new
#1394 PRO mode
2 parents ea4c99b + ec7fe2c commit ef6ac48

File tree

400 files changed

+41697
-8121
lines changed

Some content is hidden

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

400 files changed

+41697
-8121
lines changed

CHANGELOG.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,28 @@
1+
## [4.0.0 Beta 1](https://github.com/sds100/KeyMapper/releases/tag/v4.0.0-beta.01)
2+
3+
#### TO BE RELEASED
4+
5+
## Added
6+
7+
- #761 Detect keys with scancodes. Key Mapper will do this automatically if the key code is unknown
8+
or you record different physical keys from the same device with the same key code.
9+
- Redesign the Settings screen
10+
11+
## Removed
12+
13+
- The key event relay service is now also used on all Android versions below Android 14. The
14+
broadcast receiver method is no longer used.
15+
- Minimum supported Android version is now 8.0. Less than 1% of users are on older versions than
16+
this and dropping support simplifies the codebase and maintenance.
17+
- Dropped support for showing a keyboard picker notification and automatically showing it when a
18+
device connects. This is only supported on Android 8.1 and is extra work to maintain it.
19+
- Dropped support for rerouting key events on Android 11. This was a workaround for a specific bug
20+
in Android 11 which fewer than 10% of users are using and less are probably using that feature.
21+
22+
## Fixed
23+
24+
- Restoring subgroups works and does not freeze Key Mapper
25+
126
## [3.2.1](https://github.com/sds100/KeyMapper/releases/tag/v3.2.1)
227

328
#### 03 Sept 2025

CREDITS.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ Many thanks to...
1313
- @[MFlisar](https://github.com/MFlisar) for their [drag and select](https://github.com/MFlisar/DragSelectRecyclerView) library.
1414
- @[RikkaApps](https://github.com/RikkaApps) for Shizuku! It is amazing.
1515
- @[canopas](https://github.com/canopas) for their Jetpack Compose Tap Target library https://github.com/canopas/compose-intro-showcase.
16+
- @[topjohnwu](https://github.com/topjohnwu) for Magisk
17+
and [libsu](https://github.com/topjohnwu/libsu).
1618

1719
[salomonbrys]: https://github.com/salomonbrys
1820
[Kotson]: https://github.com/salomonbrys/Kotson

api/src/main/java/io/github/sds100/keymapper/api/KeyEventRelayService.kt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,10 @@ import java.util.concurrent.ConcurrentHashMap
2121
*
2222
* This was originally implemented in issue #850 for the action to answer phone calls
2323
* because Android doesn't pass volume down key events to the accessibility service
24-
* when the phone is ringing or it is in a phone call.
24+
* when the phone is ringing or it is in a phone call. Later, in Android 14 this relay must be
25+
* used because they also introduced a 1-second delay to context-registered broadcast receivers.
26+
* And who knows what other restrictions will be added in the future :)
27+
*
2528
* The accessibility service registers a callback and the input method service
2629
* sends the key events.
2730
*/

app/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ dependencies {
141141
implementation(project(":base"))
142142
implementation(project(":api"))
143143
implementation(project(":data"))
144+
implementation(project(":sysbridge"))
144145
implementation(project(":system"))
145146
compileOnly(project(":systemstubs"))
146147

app/proguard-rules.pro

Lines changed: 133 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,4 +98,136 @@
9898

9999
-keep class com.google.gson.reflect.TypeToken
100100
-keep class * extends com.google.gson.reflect.TypeToken
101-
-keep public class * implements java.lang.reflect.Type
101+
-keep public class * implements java.lang.reflect.Type
102+
103+
-keep class io.github.sds100.keymapper.sysbridge.service.SystemBridge {
104+
public <methods>;
105+
native <methods>;
106+
static <methods>;
107+
<init>(...);
108+
}
109+
110+
# Keep all AIDL interface classes and their methods
111+
-keep class io.github.sds100.keymapper.sysbridge.ISystemBridge** { *; }
112+
-keep class io.github.sds100.keymapper.sysbridge.IEvdevCallback** { *; }
113+
-keep class io.github.sds100.keymapper.sysbridge.IShizukuStarterService** { *; }
114+
115+
# Keep binder provider classes
116+
-keep class io.github.sds100.keymapper.sysbridge.provider.** { *; }
117+
118+
# Keep classes accessed via reflection or from system services
119+
-keep class io.github.sds100.keymapper.sysbridge.utils.** { *; }
120+
121+
# Keep native method signatures
122+
-keepclasseswithmembernames class * {
123+
native <methods>;
124+
}
125+
126+
# Keep classes that might be accessed via ContentProvider
127+
-keep class io.github.sds100.keymapper.sysbridge.** extends android.content.ContentProvider { *; }
128+
129+
# Keep parcelable classes used in AIDL
130+
-keep class io.github.sds100.keymapper.common.models.EvdevDeviceHandle { *; }
131+
132+
# Keep all rikka.hidden classes and interfaces as they contain AIDL files
133+
-keep class rikka.hidden.** { *; }
134+
-keep interface rikka.hidden.** { *; }
135+
136+
# Keep Android system API classes and interfaces that rikka.hidden depends on
137+
# android.app package classes
138+
-keep class android.app.ActivityManagerNative { *; }
139+
-keep class android.app.ActivityTaskManager$RootTaskInfo { *; }
140+
-keep class android.app.ContentProviderHolder { *; }
141+
-keep class android.app.IActivityManager** { *; }
142+
-keep class android.app.IApplicationThread** { *; }
143+
-keep class android.app.IProcessObserver** { *; }
144+
-keep class android.app.ITaskStackListener** { *; }
145+
-keep class android.app.IUidObserver** { *; }
146+
-keep class android.app.ProfilerInfo { *; }
147+
148+
# android.content package classes
149+
-keep class android.content.IContentProvider** { *; }
150+
-keep class android.content.IIntentReceiver** { *; }
151+
152+
# android.content.pm package classes
153+
-keep class android.content.pm.IPackageManager** { *; }
154+
-keep class android.content.pm.IPackageInstaller** { *; }
155+
-keep class android.content.pm.ILauncherApps** { *; }
156+
-keep class android.content.pm.IOnAppsChangedListener** { *; }
157+
-keep class android.content.pm.IPackageInstallerCallback** { *; }
158+
-keep class android.content.pm.ParceledListSlice { *; }
159+
-keep class android.content.pm.UserInfo { *; }
160+
161+
# android.hardware package classes
162+
-keep class android.hardware.input.IInputManager** { *; }
163+
-keep class android.hardware.display.IDisplayManager** { *; }
164+
-keep class android.hardware.display.IDisplayManagerCallback** { *; }
165+
166+
# android.os package classes
167+
-keep class android.os.BatteryProperty { *; }
168+
-keep class android.os.IBatteryPropertiesRegistrar** { *; }
169+
-keep class android.os.IDeviceIdleController { *; }
170+
-keep class android.os.IDeviceIdleController** { *; }
171+
-keep class android.os.IUserManager { *; }
172+
-keep class android.os.IUserManager** { *; }
173+
-keep class android.os.RemoteCallback** { *; }
174+
-keep class android.os.ServiceManager { *; }
175+
176+
# android.view package classes
177+
-keep class android.view.DisplayInfo { *; }
178+
-keep class android.view.IWindowManager** { *; }
179+
180+
# android.permission package classes
181+
-keep class android.permission.IPermissionManager** { *; }
182+
183+
# android.net package classes
184+
-keep class android.net.wifi.IWifiManager** { *; }
185+
186+
# com.android.internal package classes
187+
-keep class com.android.internal.app.IAppOpsActiveCallback** { *; }
188+
-keep class com.android.internal.app.IAppOpsNotedCallback** { *; }
189+
-keep class com.android.internal.app.IAppOpsService** { *; }
190+
-keep class com.android.internal.policy.IKeyguardLockedStateListener** { *; }
191+
192+
# Keep all Android AIDL interfaces (they implement IInterface)
193+
-keep class android.** implements android.os.IInterface { *; }
194+
195+
# Keep Android system service stubs and natives
196+
-keep class android.**Native { *; }
197+
-keep class android.**$Stub** { *; }
198+
-keep class android.**$Proxy** { *; }
199+
200+
# Keep Android hidden/internal classes that might be accessed via reflection
201+
-dontwarn android.app.ActivityManagerNative
202+
-dontwarn android.app.ActivityTaskManager$**
203+
-dontwarn android.app.ContentProviderHolder
204+
-dontwarn android.app.IActivityManager**
205+
-dontwarn android.app.IApplicationThread**
206+
-dontwarn android.app.IProcessObserver**
207+
-dontwarn android.app.ITaskStackListener**
208+
-dontwarn android.app.IUidObserver**
209+
-dontwarn android.app.ProfilerInfo
210+
-dontwarn android.app.AppOpsManager$**
211+
-dontwarn android.content.IContentProvider**
212+
-dontwarn android.content.IIntentReceiver**
213+
-dontwarn android.content.pm.ILauncherApps**
214+
-dontwarn android.content.pm.IOnAppsChangedListener**
215+
-dontwarn android.content.pm.IPackageInstallerCallback**
216+
-dontwarn android.content.pm.ParceledListSlice
217+
-dontwarn android.content.pm.UserInfo
218+
-dontwarn android.content.pm.PackageManagerHidden
219+
-dontwarn android.hardware.display.IDisplayManager**
220+
-dontwarn android.hardware.display.IDisplayManagerCallback**
221+
-dontwarn android.os.BatteryProperty
222+
-dontwarn android.os.IBatteryPropertiesRegistrar**
223+
-dontwarn android.os.IDeviceIdleController
224+
-dontwarn android.os.IDeviceIdleController**
225+
-dontwarn android.os.IUserManager
226+
-dontwarn android.os.IUserManager**
227+
-dontwarn android.os.RemoteCallback**
228+
-dontwarn android.os.ServiceManager
229+
-dontwarn android.os.UserHandle$**
230+
-dontwarn android.view.DisplayInfo
231+
-dontwarn android.view.IWindowManager**
232+
-dontwarn com.android.internal.app.**
233+
-dontwarn com.android.internal.policy.**

app/src/main/AndroidManifest.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,16 @@
55
<uses-feature
66
android:name="android.software.leanback"
77
android:required="false" />
8+
89
<uses-feature
910
android:name="android.hardware.touchscreen"
1011
android:required="false" />
1112

13+
<!-- This is required because sysbridge has a min sdk of 29 since it depends
14+
on the binder-ndk library. Building will fail without this because the min sdk
15+
of the app is 26.-->
16+
<uses-sdk tools:overrideLibrary="io.github.sds100.keymapper.sysbridge" />
17+
1218
<application
1319
android:name="io.github.sds100.keymapper.KeyMapperApp"
1420
android:allowBackup="true"
@@ -19,6 +25,7 @@
1925
android:supportsRtl="true"
2026
android:theme="@style/AppTheme.NoActionBar"
2127
android:usesCleartextTraffic="true"
28+
android:extractNativeLibs="true"
2229
tools:ignore="GoogleAppIndexingWarning,MissingTvBanner">
2330
<!-- Allow clear text traffic due to the HTTP Request Action. -->
2431

app/src/main/java/io/github/sds100/keymapper/MainFragment.kt

Lines changed: 65 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -24,20 +24,24 @@ import androidx.navigation.compose.composable
2424
import androidx.navigation.compose.rememberNavController
2525
import dagger.hilt.android.AndroidEntryPoint
2626
import io.github.sds100.keymapper.base.BaseMainNavHost
27-
import io.github.sds100.keymapper.base.actions.ChooseActionScreen
28-
import io.github.sds100.keymapper.base.actions.ChooseActionViewModel
27+
import io.github.sds100.keymapper.base.actions.ActionsScreen
28+
import io.github.sds100.keymapper.base.actions.ConfigActionsViewModel
2929
import io.github.sds100.keymapper.base.compose.KeyMapperTheme
30+
import io.github.sds100.keymapper.base.constraints.ConfigConstraintsViewModel
31+
import io.github.sds100.keymapper.base.constraints.ConstraintsScreen
3032
import io.github.sds100.keymapper.base.databinding.FragmentComposeBinding
3133
import io.github.sds100.keymapper.base.home.HomeKeyMapListScreen
34+
import io.github.sds100.keymapper.base.keymaps.ConfigKeyMapScreen
35+
import io.github.sds100.keymapper.base.keymaps.ConfigKeyMapViewModel
36+
import io.github.sds100.keymapper.base.keymaps.KeyMapOptionsScreen
3237
import io.github.sds100.keymapper.base.utils.navigation.NavDestination
3338
import io.github.sds100.keymapper.base.utils.navigation.NavigationProviderImpl
3439
import io.github.sds100.keymapper.base.utils.navigation.SetupNavigation
3540
import io.github.sds100.keymapper.base.utils.navigation.handleRouteArgs
3641
import io.github.sds100.keymapper.base.utils.navigation.setupFragmentNavigation
37-
import io.github.sds100.keymapper.base.utils.ui.DialogProviderImpl
3842
import io.github.sds100.keymapper.home.HomeViewModel
39-
import io.github.sds100.keymapper.keymaps.ConfigKeyMapScreen
40-
import io.github.sds100.keymapper.keymaps.ConfigKeyMapViewModel
43+
import io.github.sds100.keymapper.trigger.ConfigTriggerViewModel
44+
import io.github.sds100.keymapper.trigger.TriggerScreen
4145
import javax.inject.Inject
4246

4347
@AndroidEntryPoint
@@ -46,9 +50,6 @@ class MainFragment : Fragment() {
4650
@Inject
4751
lateinit var navigationProvider: NavigationProviderImpl
4852

49-
@Inject
50-
lateinit var dialogProvider: DialogProviderImpl
51-
5253
override fun onCreate(savedInstanceState: Bundle?) {
5354
super.onCreate(savedInstanceState)
5455

@@ -107,45 +108,84 @@ class MainFragment : Fragment() {
107108
}
108109

109110
composable<NavDestination.NewKeyMap> { backStackEntry ->
110-
val viewModel: ConfigKeyMapViewModel = hiltViewModel()
111+
val keyMapViewModel: ConfigKeyMapViewModel = hiltViewModel()
112+
val triggerViewModel: ConfigTriggerViewModel = hiltViewModel()
113+
val actionsViewModel: ConfigActionsViewModel = hiltViewModel()
114+
val constraintsViewModel: ConfigConstraintsViewModel = hiltViewModel()
115+
val snackbarHostState = remember { SnackbarHostState() }
111116

112117
backStackEntry.handleRouteArgs<NavDestination.NewKeyMap> { args ->
113-
viewModel.loadNewKeyMap(groupUid = args.groupUid)
118+
keyMapViewModel.loadNewKeyMap(groupUid = args.groupUid)
114119

115120
if (args.showAdvancedTriggers) {
116-
viewModel.configTriggerViewModel.showAdvancedTriggersBottomSheet = true
121+
triggerViewModel.showAdvancedTriggersBottomSheet = true
117122
}
118123
}
119124

120125
ConfigKeyMapScreen(
121126
modifier = Modifier.fillMaxSize(),
122-
viewModel = viewModel,
127+
snackbarHostState = snackbarHostState,
128+
keyMapViewModel = keyMapViewModel,
129+
triggerScreen = {
130+
TriggerScreen(Modifier.fillMaxSize(), triggerViewModel)
131+
},
132+
actionsScreen = {
133+
ActionsScreen(Modifier.fillMaxSize(), actionsViewModel)
134+
},
135+
constraintsScreen = {
136+
ConstraintsScreen(
137+
Modifier.fillMaxSize(),
138+
constraintsViewModel,
139+
snackbarHostState,
140+
)
141+
},
142+
optionsScreen = {
143+
KeyMapOptionsScreen(
144+
Modifier.fillMaxSize(),
145+
triggerViewModel.optionsViewModel,
146+
)
147+
},
123148
)
124149
}
125150

126151
composable<NavDestination.OpenKeyMap> { backStackEntry ->
127-
val viewModel: ConfigKeyMapViewModel = hiltViewModel()
152+
val keyMapViewModel: ConfigKeyMapViewModel = hiltViewModel()
153+
val triggerViewModel: ConfigTriggerViewModel = hiltViewModel()
154+
val actionsViewModel: ConfigActionsViewModel = hiltViewModel()
155+
val constraintsViewModel: ConfigConstraintsViewModel = hiltViewModel()
156+
val snackbarHostState = remember { SnackbarHostState() }
128157

129158
backStackEntry.handleRouteArgs<NavDestination.OpenKeyMap> { args ->
130-
viewModel.loadKeyMap(uid = args.keyMapUid)
159+
keyMapViewModel.loadKeyMap(uid = args.keyMapUid)
131160

132161
if (args.showAdvancedTriggers) {
133-
viewModel.configTriggerViewModel.showAdvancedTriggersBottomSheet = true
162+
triggerViewModel.showAdvancedTriggersBottomSheet = true
134163
}
135164
}
136165

137166
ConfigKeyMapScreen(
138167
modifier = Modifier.fillMaxSize(),
139-
viewModel = viewModel,
140-
)
141-
}
142-
143-
composable<NavDestination.ChooseAction> {
144-
val viewModel: ChooseActionViewModel = hiltViewModel()
145-
146-
ChooseActionScreen(
147-
modifier = Modifier.fillMaxSize(),
148-
viewModel = viewModel,
168+
snackbarHostState = snackbarHostState,
169+
keyMapViewModel = keyMapViewModel,
170+
triggerScreen = {
171+
TriggerScreen(Modifier.fillMaxSize(), triggerViewModel)
172+
},
173+
actionsScreen = {
174+
ActionsScreen(Modifier.fillMaxSize(), actionsViewModel)
175+
},
176+
constraintsScreen = {
177+
ConstraintsScreen(
178+
Modifier.fillMaxSize(),
179+
constraintsViewModel,
180+
snackbarHostState,
181+
)
182+
},
183+
optionsScreen = {
184+
KeyMapOptionsScreen(
185+
Modifier.fillMaxSize(),
186+
triggerViewModel.optionsViewModel,
187+
)
188+
},
149189
)
150190
}
151191
}

app/src/main/java/io/github/sds100/keymapper/home/HomeViewModel.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ package io.github.sds100.keymapper.home
33
import dagger.hilt.android.lifecycle.HiltViewModel
44
import io.github.sds100.keymapper.base.backup.BackupRestoreMappingsUseCase
55
import io.github.sds100.keymapper.base.home.BaseHomeViewModel
6+
import io.github.sds100.keymapper.base.home.ListKeyMapsUseCase
67
import io.github.sds100.keymapper.base.home.ShowHomeScreenAlertsUseCase
7-
import io.github.sds100.keymapper.base.keymaps.ListKeyMapsUseCase
88
import io.github.sds100.keymapper.base.keymaps.PauseKeyMapsUseCase
99
import io.github.sds100.keymapper.base.onboarding.OnboardingUseCase
1010
import io.github.sds100.keymapper.base.sorting.SortKeyMapsUseCase

0 commit comments

Comments
 (0)