manager: some ui changes

* Steeing: add enhanced security dropdown
* Settings: allow change module update check
* Settings: allow always enable/disable feat
* misc: update all deps

---------

Co-authored-by: weishu <twsxtd@gmail.com>
Co-authored-by: Ylarod <me@ylarod.cn>
Co-authored-by: YuKongA <70465933+YuKongA@users.noreply.github.com>
Co-authored-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
This commit is contained in:
ShirkNeko
2025-11-06 22:54:43 +08:00
parent 132e9ef8ed
commit 3dde6d9a25
15 changed files with 337 additions and 82 deletions

View File

@@ -2,7 +2,6 @@
import com.android.build.gradle.internal.api.BaseVariantOutputImpl
import com.android.build.gradle.tasks.PackageAndroidArtifact
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins {
alias(libs.plugins.agp.app)
@@ -17,6 +16,7 @@ plugins {
val managerVersionCode: Int by rootProject.extra
val managerVersionName: String by rootProject.extra
val androidCmakeVersion: String by rootProject.extra
apksign {
storeFileProperty = "KEYSTORE_FILE"
@@ -57,10 +57,6 @@ android {
prefab = true
}
kotlin {
jvmToolchain(21)
}
packaging {
jniLibs {
useLegacyPackaging = true
@@ -78,7 +74,8 @@ android {
externalNativeBuild {
cmake {
path("src/main/cpp/CMakeLists.txt")
path = file("src/main/cpp/CMakeLists.txt")
version = androidCmakeVersion
}
}
@@ -126,6 +123,7 @@ dependencies {
implementation(libs.androidx.compose.ui.tooling.preview)
implementation(libs.androidx.foundation)
implementation(libs.androidx.documentfile)
implementation(libs.androidx.compose.foundation)
debugImplementation(libs.androidx.compose.ui.test.manifest)
debugImplementation(libs.androidx.compose.ui.tooling)

View File

@@ -310,6 +310,14 @@ NativeBridge(setKernelUmountEnabled, jboolean, jboolean enabled) {
return set_kernel_umount_enabled(enabled);
}
NativeBridgeNP(isEnhancedSecurityEnabled, jboolean) {
return is_enhanced_security_enabled();
}
NativeBridge(setEnhancedSecurityEnabled, jboolean, jboolean enabled) {
return set_enhanced_security_enabled(enabled);
}
// Check if KPM is enabled
NativeBridgeNP(isKPMEnabled, jboolean) {
return is_KPM_enable();

View File

@@ -215,6 +215,22 @@ bool is_kernel_umount_enabled() {
return value != 0;
}
bool set_enhanced_security_enabled(bool enabled) {
return set_feature(KSU_FEATURE_ENHANCED_SECURITY, enabled ? 1 : 0);
}
bool is_enhanced_security_enabled() {
uint64_t value = 0;
bool supported = false;
if (!get_feature(KSU_FEATURE_ENHANCED_SECURITY, &value, &supported)) {
return false;
}
if (!supported) {
return false;
}
return value != 0;
}
void get_full_version(char* buff) {
struct ksu_get_full_version_cmd cmd = {0};
if (ksuctl(KSU_IOCTL_GET_FULL_VERSION, &cmd) == 0) {

View File

@@ -131,6 +131,7 @@ bool clear_uid_scanner_environment();
enum ksu_feature_id {
KSU_FEATURE_SU_COMPAT = 0,
KSU_FEATURE_KERNEL_UMOUNT = 1,
KSU_FEATURE_ENHANCED_SECURITY = 2,
};
// Generic feature API
@@ -208,6 +209,11 @@ bool is_su_enabled();
bool set_kernel_umount_enabled(bool enabled);
bool is_kernel_umount_enabled();
// Enhanced security
bool set_enhanced_security_enabled(bool enabled);
bool is_enhanced_security_enabled();
// Other command structures
struct ksu_get_full_version_cmd {
char version_full[KSU_FULL_VERSION_STRING]; // Output: full version string

View File

@@ -109,6 +109,15 @@ object Natives {
external fun isKernelUmountEnabled(): Boolean
external fun setKernelUmountEnabled(enabled: Boolean): Boolean
/**
* Enhanced security can be enabled/disabled.
* 0: disabled
* 1: enabled
* negative : error
*/
external fun isEnhancedSecurityEnabled(): Boolean
external fun setEnhancedSecurityEnabled(enabled: Boolean): Boolean
external fun isKPMEnabled(): Boolean
external fun getHookType(): String

View File

@@ -10,14 +10,20 @@ import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.animation.*
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.interaction.collectIsPressedAsState
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.Undo
import androidx.compose.material.icons.filled.*
import androidx.compose.material.icons.rounded.EnhancedEncryption
import androidx.compose.material.icons.rounded.FolderDelete
import androidx.compose.material.icons.rounded.RemoveCircle
import androidx.compose.material.icons.rounded.RemoveModerator
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
@@ -130,60 +136,147 @@ fun SettingScreen(navigator: DestinationsNavigator) {
}
)
// 卸载模块开关
var umountChecked by rememberSaveable {
mutableStateOf(Natives.isDefaultUmountModules())
}
SwitchItem(
icon = Icons.Filled.FolderDelete,
title = stringResource(R.string.settings_umount_modules_default),
summary = stringResource(R.string.settings_umount_modules_default_summary),
checked = umountChecked,
onCheckedChange = { enabled ->
if (Natives.setDefaultUmountModules(enabled)) {
umountChecked = enabled
}
}
val modeItems = listOf(
stringResource(id = R.string.settings_mode_default),
stringResource(id = R.string.settings_mode_temp_enable),
stringResource(id = R.string.settings_mode_always_enable),
)
// SU 禁用开关
var isSuDisabled by rememberSaveable {
mutableStateOf(!Natives.isSuEnabled())
}
SwitchItem(
icon = Icons.Filled.RemoveModerator,
title = stringResource(R.string.settings_disable_su),
summary = stringResource(R.string.settings_disable_su_summary),
checked = isSuDisabled,
onCheckedChange = { enabled ->
val shouldEnable = !enabled
if (Natives.setSuEnabled(shouldEnable)) {
isSuDisabled = enabled
}
}
)
// 禁用内核卸载开关
if (Natives.version >= Natives.MINIMAL_NEW_IOCTL_KERNEL) {
var isKernelUmountDisabled by rememberSaveable {
mutableStateOf(!Natives.isKernelUmountEnabled())
}
SwitchItem(
icon = Icons.Rounded.FolderDelete,
title = stringResource(id = R.string.settings_disable_kernel_umount),
summary = stringResource(id = R.string.settings_disable_kernel_umount_summary),
checked = isKernelUmountDisabled,
onCheckedChange = { checked: Boolean ->
val shouldEnable = !checked
if (Natives.setKernelUmountEnabled(shouldEnable)) {
isKernelUmountDisabled = !shouldEnable
}
}
var enhancedSecurityMode by rememberSaveable {
mutableIntStateOf(
prefs.getInt(
"enhanced_security_mode", if (Natives.isEnhancedSecurityEnabled()) 1 else 0
)
)
}
SettingDropdown(
icon = Icons.Rounded.EnhancedEncryption,
title = stringResource(id = R.string.settings_enable_enhanced_security),
summary = stringResource(id = R.string.settings_enable_enhanced_security_summary),
items = modeItems,
selectedIndex = enhancedSecurityMode,
onSelectedIndexChange = { index ->
when (index) {
// Default: disable and save to persist
0 -> if (Natives.setEnhancedSecurityEnabled(false)) {
execKsud("feature save", true)
prefs.edit { putInt("enhanced_security_mode", 0) }
enhancedSecurityMode = 0
}
// Temporarily enable: save disabled state first, then enable
1 -> if (Natives.setEnhancedSecurityEnabled(false)) {
execKsud("feature save", true)
if (Natives.setEnhancedSecurityEnabled(true)) {
prefs.edit { putInt("enhanced_security_mode", 1) }
enhancedSecurityMode = 1
}
}
// Permanently enable: enable and save
2 -> if (Natives.setEnhancedSecurityEnabled(true)) {
execKsud("feature save", true)
prefs.edit { putInt("enhanced_security_mode", 2) }
enhancedSecurityMode = 2
}
}
}
)
var suCompatMode by rememberSaveable {
mutableIntStateOf(
prefs.getInt(
"su_compat_mode", if (!Natives.isSuEnabled()) 1 else 0
)
)
}
SettingDropdown(
icon = Icons.Rounded.RemoveModerator,
title = stringResource(id = R.string.settings_disable_su),
summary = stringResource(id = R.string.settings_disable_su_summary),
items = modeItems,
selectedIndex = suCompatMode,
onSelectedIndexChange = { index ->
when (index) {
// Default: enable and save to persist
0 -> if (Natives.setSuEnabled(true)) {
execKsud("feature save", true)
prefs.edit { putInt("su_compat_mode", 0) }
suCompatMode = 0
}
// Temporarily disable: save enabled state first, then disable
1 -> if (Natives.setSuEnabled(true)) {
execKsud("feature save", true)
if (Natives.setSuEnabled(false)) {
prefs.edit { putInt("su_compat_mode", 1) }
suCompatMode = 1
}
}
// Permanently disable: disable and save
2 -> if (Natives.setSuEnabled(false)) {
execKsud("feature save", true)
prefs.edit { putInt("su_compat_mode", 2) }
suCompatMode = 2
}
}
}
)
var kernelUmountMode by rememberSaveable {
mutableIntStateOf(
prefs.getInt(
"kernel_umount_mode", if (!Natives.isKernelUmountEnabled()) 1 else 0
)
)
}
SettingDropdown(
icon = Icons.Rounded.RemoveCircle,
title = stringResource(id = R.string.settings_disable_kernel_umount),
summary = stringResource(id = R.string.settings_disable_kernel_umount_summary),
items = modeItems,
selectedIndex = kernelUmountMode,
onSelectedIndexChange = { index ->
when (index) {
// Default: enable and save to persist
0 -> if (Natives.setKernelUmountEnabled(true)) {
execKsud("feature save", true)
prefs.edit { putInt("kernel_umount_mode", 0) }
kernelUmountMode = 0
}
// Temporarily disable: save enabled state first, then disable
1 -> if (Natives.setKernelUmountEnabled(true)) {
execKsud("feature save", true)
if (Natives.setKernelUmountEnabled(false)) {
prefs.edit { putInt("kernel_umount_mode", 1) }
kernelUmountMode = 1
}
}
// Permanently disable: disable and save
2 -> if (Natives.setKernelUmountEnabled(false)) {
execKsud("feature save", true)
prefs.edit { putInt("kernel_umount_mode", 2) }
kernelUmountMode = 2
}
}
}
)
// 卸载模块开关
var umountChecked by rememberSaveable { mutableStateOf(Natives.isDefaultUmountModules()) }
SwitchItem(
icon = Icons.Rounded.FolderDelete,
title = stringResource(id = R.string.settings_umount_modules_default),
summary = stringResource(id = R.string.settings_umount_modules_default_summary),
checked = umountChecked,
onCheckedChange = {
if (Natives.setDefaultUmountModules(it)) {
umountChecked = it
}
}
)
// 强制签名验证开关
@@ -966,4 +1059,104 @@ private fun UidScannerSection(
}
)
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun SettingDropdown(
icon: ImageVector,
title: String,
summary: String,
items: List<String>,
selectedIndex: Int,
leftAction: (@Composable () -> Unit)? = null,
onSelectedIndexChange: (Int) -> Unit
) {
var showDialog by remember { mutableStateOf(false) }
val selectedItemText = items.getOrNull(selectedIndex) ?: ""
Row(
modifier = Modifier
.fillMaxWidth()
.clickable { showDialog = true }
.padding(horizontal = SPACING_LARGE, vertical = 12.dp),
verticalAlignment = Alignment.Top
) {
if (leftAction != null) {
leftAction()
} else {
Icon(
imageVector = icon,
contentDescription = null,
tint = MaterialTheme.colorScheme.primary,
modifier = Modifier
.padding(end = SPACING_LARGE)
.size(24.dp)
)
}
Column(modifier = Modifier.weight(1f)) {
Text(
text = title,
style = MaterialTheme.typography.titleMedium
)
Spacer(modifier = Modifier.height(SPACING_SMALL))
Text(
text = summary,
style = MaterialTheme.typography.bodyMedium
)
Spacer(modifier = Modifier.height(SPACING_SMALL))
Text(
text = selectedItemText,
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
}
Icon(
imageVector = Icons.Filled.ArrowForward,
contentDescription = null,
tint = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier.size(24.dp)
)
}
if (showDialog) {
AlertDialog(
onDismissRequest = { showDialog = false },
title = { Text(title) },
text = {
LazyColumn(
modifier = Modifier.fillMaxWidth(),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
items(items) { item ->
val index = items.indexOf(item)
Row(
modifier = Modifier
.fillMaxWidth()
.clickable {
onSelectedIndexChange(index)
showDialog = false
}
.padding(vertical = 12.dp),
verticalAlignment = Alignment.CenterVertically
) {
RadioButton(
selected = selectedIndex == index,
onClick = null
)
Spacer(modifier = Modifier.width(16.dp))
Text(text = item)
}
}
}
},
confirmButton = {
TextButton(onClick = { showDialog = false }) {
Text(stringResource(android.R.string.cancel))
}
}
)
}
}

View File

@@ -165,9 +165,14 @@
<string name="home_device_model">设备</string>
<string name="su_not_allowed">不允许授予 %s 超级用户权限</string>
<string name="settings_disable_su">禁用 su 兼容性</string>
<string name="settings_disable_su_summary">临时禁止任何应用程序通过 su 命令获取 Root 权限(现有Root 进程不受影响)</string>
<string name="settings_disable_su_summary">禁止任何应用通过 su 命令获取 root 权限(已运行root 进程不受影响)</string>
<string name="settings_disable_kernel_umount">关闭内核 umount</string>
<string name="settings_disable_kernel_umount_summary">临时关闭 KernelSU 控制的内核级 umount 行为。</string>
<string name="settings_disable_kernel_umount_summary">关闭 KernelSU 控制的内核级 umount 行为。</string>
<string name="settings_enable_enhanced_security">启用增强安全</string>
<string name="settings_enable_enhanced_security_summary">启用更严格的安全策略。</string>
<string name="settings_mode_default">默认</string>
<string name="settings_mode_temp_enable">临时启用</string>
<string name="settings_mode_always_enable">始终启用</string>
<string name="module_install_multiple_confirm_with_names">确定要安装以下 %1$d 个模块吗?\n\n%2$s</string>
<string name="more_settings">更多设置</string>
<string name="selinux">SELinux</string>

View File

@@ -167,9 +167,14 @@
<string name="home_device_model">Device model</string>
<string name="su_not_allowed">Granting superuser to %s is not allowed</string>
<string name="settings_disable_su">Disable su compatibility</string>
<string name="settings_disable_su_summary">Temporarily disable any applications from obtaining root privileges via the su command (Existing root processes will not be affected)</string>
<string name="settings_disable_su_summary">Disable the ability of any app to gain root privileges via the su command (existing root processes won\'t be affected).</string>
<string name="settings_disable_kernel_umount">Disable kernel umount</string>
<string name="settings_disable_kernel_umount_summary">Temporarily disable kernel-level umount behavior controlled by KernelSU.</string>
<string name="settings_disable_kernel_umount_summary">Disable kernel-level umount behavior controlled by KernelSU.</string>
<string name="settings_enable_enhanced_security">Enable enhanced security</string>
<string name="settings_enable_enhanced_security_summary">Enable stricter security policies.</string>
<string name="settings_mode_default">Default</string>
<string name="settings_mode_temp_enable">Temporarily enable</string>
<string name="settings_mode_always_enable">Permanently enable</string>
<string name="module_install_multiple_confirm_with_names">Sure you want to install the following %1$d modules? \n\n%2$s</string>
<string name="more_settings">More settings</string>
<string name="selinux">SELinux</string>