Skip to content

Commit cc06a17

Browse files
authored
Merge pull request #2010 from keymapperorg/develop
Version 4.0.1
2 parents 7950ddb + ec6756f commit cc06a17

File tree

16 files changed

+118
-129
lines changed

16 files changed

+118
-129
lines changed

CHANGELOG.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,15 @@
1+
## [4.0.1](https://github.com/sds100/KeyMapper/releases/tag/v4.0.1)
2+
3+
#### 01 February 2026
4+
5+
## Fixed
6+
- #2007 Volume up/down action bottom sheet is cut off on some devices.
7+
- #2004 Do not crash when launching wireless debugging screen on some devices.
8+
- #2005 NPE in onSaveInstanceState.
9+
- #2000 tell the user to tap OS version or build number.
10+
- #1996 Check if Night Shift is supported on a device before activating to prevent the screen going black when activated.
11+
- #1999 Use a more reliable method to check whether Shell has GRANT_RUNTIME_PERMISSIONS permission.
12+
113
## [4.0.0](https://github.com/sds100/KeyMapper/releases/tag/v4.0.0)
214

315
#### 25 January 2026

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

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ class MainFragment : Fragment() {
6060

6161
private lateinit var composeView: ComposeView
6262

63-
private lateinit var navController: NavHostController
63+
private var navController: NavHostController? = null
6464

6565
override fun onCreate(savedInstanceState: Bundle?) {
6666
super.onCreate(savedInstanceState)
@@ -98,7 +98,7 @@ class MainFragment : Fragment() {
9898
// is destroyed
9999
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
100100
setContent {
101-
SetupNavigation(navigationProvider, navController)
101+
SetupNavigation(navigationProvider, navController!!)
102102

103103
KeyMapperTheme {
104104
BaseMainNavHost(
@@ -111,10 +111,10 @@ class MainFragment : Fragment() {
111111
),
112112
),
113113
),
114-
navController = navController,
114+
navController = navController!!,
115115
setupAccessibilityServiceDelegate = setupAccessibilityServiceDelegate,
116116
composableDestinations = {
117-
composableDestinations(navController)
117+
composableDestinations(navController!!)
118118
},
119119
)
120120
}
@@ -123,15 +123,15 @@ class MainFragment : Fragment() {
123123
}
124124

125125
override fun onSaveInstanceState(outState: Bundle) {
126-
navController.saveState()?.let(outState::putAll)
126+
navController?.saveState()?.let(outState::putAll)
127127

128128
super.onSaveInstanceState(outState)
129129
}
130130

131131
override fun onDestroyView() {
132132
// onSaveInstanceState is only called when the activity's onSaveInstanceState method
133133
// is called so use our own place to save the navigation state
134-
navigationProvider.savedState = navController.saveState()
134+
navigationProvider.savedState = navController?.saveState()
135135

136136
super.onDestroyView()
137137
}

app/version.properties

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
VERSION_NAME=4.0.0
2-
VERSION_CODE=238
1+
VERSION_NAME=4.0.1
2+
VERSION_CODE=241

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package io.github.sds100.keymapper.base.actions
22

33
import android.content.pm.PackageManager
4+
import android.content.res.Resources
45
import android.os.Build
56
import io.github.sds100.keymapper.common.utils.KMError
67
import io.github.sds100.keymapper.system.SystemError
@@ -56,6 +57,18 @@ class IsActionSupportedUseCaseImpl(
5657
}
5758
}
5859

60+
if (id == ActionId.TOGGLE_NIGHT_SHIFT ||
61+
id == ActionId.ENABLE_NIGHT_SHIFT ||
62+
id == ActionId.DISABLE_NIGHT_SHIFT
63+
) {
64+
// See https://cs.android.com/android/platform/superproject/+/android-latest-release:frameworks/base/core/java/android/hardware/display/ColorDisplayManager.java;l=498;drc=787314ed22d859e510163327dd6c58b215c2f7f9
65+
val res = Resources.getSystem()
66+
val resId = res.getIdentifier("config_nightDisplayAvailable", "bool", "android")
67+
if (resId == 0 || !res.getBoolean(resId)) {
68+
return KMError.NightDisplayNotSupported
69+
}
70+
}
71+
5972
if (ActionUtils.getRequiredPermissions(id).contains(Permission.ROOT) &&
6073
!permissionAdapter.isGranted(Permission.ROOT)
6174
) {

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

Lines changed: 28 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -162,45 +162,45 @@ private fun VolumeActionBottomSheet(
162162
isChecked = state.showVolumeUi,
163163
onCheckedChange = onToggleShowVolumeUi,
164164
)
165-
}
166165

167-
Spacer(modifier = Modifier.height(16.dp))
166+
Spacer(modifier = Modifier.height(16.dp))
168167

169-
Row(
170-
modifier = Modifier
171-
.fillMaxWidth()
172-
.padding(horizontal = 16.dp),
173-
horizontalArrangement = Arrangement.SpaceBetween,
174-
verticalAlignment = Alignment.CenterVertically,
175-
) {
176-
OutlinedButton(
177-
modifier = Modifier.weight(1f),
178-
onClick = {
179-
scope.launch {
180-
sheetState.hide()
181-
onDismissRequest()
182-
}
183-
},
168+
Row(
169+
modifier = Modifier
170+
.fillMaxWidth()
171+
.padding(horizontal = 16.dp),
172+
horizontalArrangement = Arrangement.SpaceBetween,
173+
verticalAlignment = Alignment.CenterVertically,
184174
) {
185-
Text(stringResource(R.string.neg_cancel))
186-
}
175+
OutlinedButton(
176+
modifier = Modifier.weight(1f),
177+
onClick = {
178+
scope.launch {
179+
sheetState.hide()
180+
onDismissRequest()
181+
}
182+
},
183+
) {
184+
Text(stringResource(R.string.neg_cancel))
185+
}
187186

188-
Spacer(modifier = Modifier.width(16.dp))
187+
Spacer(modifier = Modifier.width(16.dp))
189188

190-
Button(
191-
modifier = Modifier.weight(1f),
192-
onClick = onDoneClick,
193-
) {
194-
Text(stringResource(R.string.pos_done))
189+
Button(
190+
modifier = Modifier.weight(1f),
191+
onClick = onDoneClick,
192+
) {
193+
Text(stringResource(R.string.pos_done))
194+
}
195195
}
196-
}
197196

198-
Spacer(Modifier.height(16.dp))
197+
Spacer(Modifier.height(16.dp))
198+
}
199199
}
200200
}
201201

202202
@OptIn(ExperimentalMaterial3Api::class)
203-
@Preview
203+
@Preview(heightDp = 400)
204204
@Composable
205205
private fun PreviewVolumeActionBottomSheet() {
206206
KeyMapperTheme {

base/src/main/java/io/github/sds100/keymapper/base/expertmode/ExpertModeScreen.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -332,7 +332,7 @@ private fun LoadedContent(
332332

333333
// Only show auto-start options and warnings when Expert Mode is started
334334
// Show USB debugging security settings warning if disabled
335-
if (state.isAdbInputSecurityEnabled == false) {
335+
if (state.showXiaomiAdbInputSecurityWarning) {
336336
UsbDebuggingSecuritySettingsCard(
337337
modifier = Modifier
338338
.fillMaxWidth()
@@ -970,7 +970,7 @@ private fun PreviewDark() {
970970
isDefaultUsbModeCompatible = true,
971971
autoStartBootChecked = true,
972972
autoStartBootEnabled = true,
973-
isAdbInputSecurityEnabled = null,
973+
showXiaomiAdbInputSecurityWarning = false,
974974
),
975975
),
976976
showInfoCard = false,
@@ -1013,7 +1013,7 @@ private fun PreviewStarted() {
10131013
isDefaultUsbModeCompatible = false,
10141014
autoStartBootChecked = false,
10151015
autoStartBootEnabled = true,
1016-
isAdbInputSecurityEnabled = null,
1016+
showXiaomiAdbInputSecurityWarning = false,
10171017
),
10181018
),
10191019
showInfoCard = false,
@@ -1062,7 +1062,7 @@ private fun PreviewUsbDebuggingSecuritySettingsCard() {
10621062
isDefaultUsbModeCompatible = true,
10631063
autoStartBootChecked = false,
10641064
autoStartBootEnabled = true,
1065-
isAdbInputSecurityEnabled = false,
1065+
showXiaomiAdbInputSecurityWarning = false,
10661066
),
10671067
),
10681068
showInfoCard = false,

base/src/main/java/io/github/sds100/keymapper/base/expertmode/ExpertModeViewModel.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -198,15 +198,15 @@ class ExpertModeViewModel @Inject constructor(
198198
private fun startedStateFlow(): Flow<ExpertModeState.Started> = combine(
199199
useCase.isAutoStartBootEnabled,
200200
useCase.isAutoStartBootAllowed,
201-
useCase.isAdbInputSecurityEnabled,
202-
) { autoStartBootChecked, autoStartBootEnabled, isAdbInputSecurityEnabled ->
201+
useCase.shellHasGrantRuntimePermissions,
202+
) { autoStartBootChecked, autoStartBootEnabled, shellHasGrantRuntimePermissions ->
203203
ExpertModeState.Started(
204204
isDefaultUsbModeCompatible =
205205
useCase.isCompatibleUsbModeSelected().valueOrNull()
206206
?: false,
207207
autoStartBootChecked = autoStartBootChecked,
208208
autoStartBootEnabled = autoStartBootEnabled,
209-
isAdbInputSecurityEnabled = isAdbInputSecurityEnabled,
209+
showXiaomiAdbInputSecurityWarning = !shellHasGrantRuntimePermissions,
210210
)
211211
}
212212
}
@@ -230,7 +230,7 @@ sealed class ExpertModeState {
230230
val isDefaultUsbModeCompatible: Boolean,
231231
val autoStartBootChecked: Boolean,
232232
val autoStartBootEnabled: Boolean,
233-
val isAdbInputSecurityEnabled: Boolean?,
233+
val showXiaomiAdbInputSecurityWarning: Boolean,
234234
) : ExpertModeState()
235235
}
236236

base/src/main/java/io/github/sds100/keymapper/base/expertmode/SystemBridgeSetupUseCase.kt

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -186,13 +186,8 @@ class SystemBridgeSetupUseCaseImpl @Inject constructor(
186186
systemBridgeSetupController.launchDeveloperOptions()
187187
}
188188

189-
@RequiresApi(Build.VERSION_CODES.R)
190-
override val isAdbInputSecurityEnabled: Flow<Boolean?> =
191-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
192-
systemBridgeSetupController.isAdbInputSecurityEnabled
193-
} else {
194-
flowOf(null)
195-
}
189+
override val shellHasGrantRuntimePermissions: Flow<Boolean> =
190+
systemBridgeSetupController.shellHasGrantRuntimePermissions
196191

197192
override fun connectWifiNetwork() {
198193
networkAdapter.connectWifiNetwork()
@@ -355,7 +350,7 @@ interface SystemBridgeSetupUseCase {
355350
fun startSystemBridgeWithAdb()
356351
fun autoStartSystemBridgeWithAdb()
357352

358-
val isAdbInputSecurityEnabled: Flow<Boolean?>
353+
val shellHasGrantRuntimePermissions: Flow<Boolean>
359354

360355
fun isCompatibleUsbModeSelected(): KMResult<Boolean>
361356

base/src/main/java/io/github/sds100/keymapper/base/utils/ErrorUtils.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,9 @@ fun KMError.getFullMessage(resourceProvider: ResourceProvider): String {
362362
R.string.error_variable_flashlight_strength_unsupported,
363363
)
364364

365+
KMError.NightDisplayNotSupported ->
366+
resourceProvider.getString(R.string.error_night_display_not_supported)
367+
365368
is KMError.FailedToModifySystemSetting ->
366369
resourceProvider.getString(
367370
R.string.error_failed_to_modify_system_setting,

base/src/main/java/io/github/sds100/keymapper/base/utils/ui/compose/SearchAppBarActions.kt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,9 @@ fun RowScope.SearchAppBarActions(
4646
}
4747

4848
DockedSearchBar(
49-
modifier = Modifier.Companion.align(Alignment.Companion.CenterVertically),
49+
modifier = Modifier.align(Alignment.CenterVertically),
5050
inputField = {
5151
SearchBarDefaults.InputField(
52-
modifier = Modifier.Companion.align(Alignment.Companion.CenterVertically),
5352
onSearch = {
5453
onQueryChange(it)
5554
isExpanded = false

0 commit comments

Comments
 (0)