diff --git a/manager/app/src/main/assets/ksu_susfs b/manager/app/src/main/assets/ksu_susfs
new file mode 100644
index 00000000..b99685f5
Binary files /dev/null and b/manager/app/src/main/assets/ksu_susfs differ
diff --git a/manager/app/src/main/java/com/sukisu/ultra/ui/component/SuSFSConfigDialog.kt b/manager/app/src/main/java/com/sukisu/ultra/ui/component/SuSFSConfigDialog.kt
new file mode 100644
index 00000000..0c77dbb0
--- /dev/null
+++ b/manager/app/src/main/java/com/sukisu/ultra/ui/component/SuSFSConfigDialog.kt
@@ -0,0 +1,327 @@
+package com.sukisu.ultra.ui.component
+
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.AutoMode
+import androidx.compose.material.icons.filled.RestoreFromTrash
+import androidx.compose.material.icons.filled.Settings
+import androidx.compose.material3.AlertDialog
+import androidx.compose.material3.Button
+import androidx.compose.material3.Card
+import androidx.compose.material3.CardDefaults
+import androidx.compose.material3.Icon
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.OutlinedButton
+import androidx.compose.material3.OutlinedTextField
+import androidx.compose.material3.Switch
+import androidx.compose.material3.Text
+import androidx.compose.material3.TextButton
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.derivedStateOf
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.unit.dp
+import com.sukisu.ultra.R
+import com.sukisu.ultra.ui.util.SuSFSManager
+import kotlinx.coroutines.launch
+
+/**
+ * SuSFS配置对话框
+ */
+@Composable
+fun SuSFSConfigDialog(
+ onDismiss: () -> Unit
+) {
+ val context = LocalContext.current
+ val coroutineScope = rememberCoroutineScope()
+
+ var unameValue by remember { mutableStateOf("") }
+ var isLoading by remember { mutableStateOf(false) }
+ var showConfirmReset by remember { mutableStateOf(false) }
+ var autoStartEnabled by remember { mutableStateOf(false) }
+ var lastAppliedValue by remember { mutableStateOf("") }
+
+ // 实时判断是否可以启用开机自启动
+ val canEnableAutoStart by remember {
+ derivedStateOf {
+ unameValue.trim().isNotBlank() && unameValue.trim() != "default"
+ }
+ }
+
+ // 加载当前配置
+ LaunchedEffect(Unit) {
+ unameValue = SuSFSManager.getUnameValue(context)
+ autoStartEnabled = SuSFSManager.isAutoStartEnabled(context)
+ lastAppliedValue = SuSFSManager.getLastAppliedValue(context)
+ }
+
+ // 当输入值变化时,自动调整开机自启动状态
+ LaunchedEffect(canEnableAutoStart) {
+ if (!canEnableAutoStart && autoStartEnabled) {
+ // 如果输入值变为default或空,自动关闭开机自启动
+ autoStartEnabled = false
+ SuSFSManager.configureAutoStart(context, false)
+ }
+ }
+
+ // 重置确认对话框
+ if (showConfirmReset) {
+ AlertDialog(
+ onDismissRequest = { showConfirmReset = false },
+ title = {
+ Text(
+ text = stringResource(R.string.susfs_reset_confirm_title),
+ style = MaterialTheme.typography.titleMedium
+ )
+ },
+ text = {
+ Text(stringResource(R.string.susfs_reset_confirm_message))
+ },
+ confirmButton = {
+ TextButton(
+ onClick = {
+ showConfirmReset = false
+ coroutineScope.launch {
+ isLoading = true
+ if (SuSFSManager.resetToDefault(context)) {
+ unameValue = "default"
+ lastAppliedValue = "default"
+ autoStartEnabled = false
+ }
+ isLoading = false
+ }
+ }
+ ) {
+ Text(stringResource(R.string.susfs_reset_confirm))
+ }
+ },
+ dismissButton = {
+ TextButton(
+ onClick = { showConfirmReset = false }
+ ) {
+ Text(stringResource(R.string.cancel))
+ }
+ }
+ )
+ }
+
+ AlertDialog(
+ onDismissRequest = onDismiss,
+ title = {
+ Row(
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Icon(
+ imageVector = Icons.Default.Settings,
+ contentDescription = null,
+ tint = MaterialTheme.colorScheme.primary
+ )
+ Spacer(modifier = Modifier.width(8.dp))
+ Text(
+ text = stringResource(R.string.susfs_config_title),
+ style = MaterialTheme.typography.titleLarge,
+ fontWeight = FontWeight.Bold
+ )
+ }
+ },
+ text = {
+ Column(
+ modifier = Modifier.fillMaxWidth()
+ ) {
+ // 说明卡片
+ Card(
+ modifier = Modifier.fillMaxWidth(),
+ colors = CardDefaults.cardColors(
+ containerColor = MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.5f)
+ ),
+ shape = RoundedCornerShape(8.dp)
+ ) {
+ Column(
+ modifier = Modifier.padding(12.dp)
+ ) {
+ Text(
+ text = stringResource(R.string.susfs_config_description),
+ style = MaterialTheme.typography.titleSmall,
+ fontWeight = FontWeight.Medium,
+ color = MaterialTheme.colorScheme.primary
+ )
+ Spacer(modifier = Modifier.height(4.dp))
+ Text(
+ text = stringResource(R.string.susfs_config_description_text),
+ style = MaterialTheme.typography.bodySmall,
+ color = MaterialTheme.colorScheme.onSurfaceVariant
+ )
+ }
+ }
+
+ Spacer(modifier = Modifier.height(16.dp))
+
+ // 输入框
+ OutlinedTextField(
+ value = unameValue,
+ onValueChange = { unameValue = it },
+ label = { Text(stringResource(R.string.susfs_uname_label)) },
+ placeholder = { Text(stringResource(R.string.susfs_uname_placeholder)) },
+ modifier = Modifier.fillMaxWidth(),
+ enabled = !isLoading,
+ singleLine = true
+ )
+
+ Spacer(modifier = Modifier.height(8.dp))
+
+ // 当前值显示
+ Text(
+ text = stringResource(R.string.susfs_current_value, SuSFSManager.getUnameValue(context)),
+ style = MaterialTheme.typography.bodySmall,
+ color = MaterialTheme.colorScheme.onSurfaceVariant
+ )
+
+ Spacer(modifier = Modifier.height(16.dp))
+
+ // 开机自启动开关
+ Card(
+ modifier = Modifier.fillMaxWidth(),
+ colors = CardDefaults.cardColors(
+ containerColor = if (canEnableAutoStart) {
+ MaterialTheme.colorScheme.surface
+ } else {
+ MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.3f)
+ }
+ ),
+ shape = RoundedCornerShape(8.dp)
+ ) {
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(16.dp),
+ horizontalArrangement = Arrangement.SpaceBetween,
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Column(
+ modifier = Modifier.weight(1f)
+ ) {
+ Row(
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Icon(
+ imageVector = Icons.Default.AutoMode,
+ contentDescription = null,
+ tint = if (canEnableAutoStart) {
+ MaterialTheme.colorScheme.primary
+ } else {
+ MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.5f)
+ },
+ modifier = Modifier.padding(end = 8.dp)
+ )
+ Text(
+ text = stringResource(R.string.susfs_autostart_title),
+ style = MaterialTheme.typography.titleSmall,
+ fontWeight = FontWeight.Medium,
+ color = if (canEnableAutoStart) {
+ MaterialTheme.colorScheme.onSurface
+ } else {
+ MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.5f)
+ }
+ )
+ }
+ Spacer(modifier = Modifier.height(4.dp))
+ Text(
+ text = if (canEnableAutoStart) {
+ stringResource(R.string.susfs_autostart_description)
+ } else {
+ stringResource(R.string.susfs_autostart_tis)
+ },
+ style = MaterialTheme.typography.bodySmall,
+ color = MaterialTheme.colorScheme.onSurfaceVariant.copy(
+ alpha = if (canEnableAutoStart) 1f else 0.5f
+ )
+ )
+ }
+ Switch(
+ checked = autoStartEnabled,
+ onCheckedChange = { enabled ->
+ if (canEnableAutoStart) {
+ coroutineScope.launch {
+ isLoading = true
+ if (SuSFSManager.configureAutoStart(context, enabled)) {
+ autoStartEnabled = enabled
+ }
+ isLoading = false
+ }
+ }
+ },
+ enabled = !isLoading && canEnableAutoStart
+ )
+ }
+ }
+
+ Spacer(modifier = Modifier.height(16.dp))
+
+ // 重置按钮
+ OutlinedButton(
+ onClick = { showConfirmReset = true },
+ modifier = Modifier.fillMaxWidth(),
+ enabled = !isLoading
+ ) {
+ Icon(
+ imageVector = Icons.Default.RestoreFromTrash,
+ contentDescription = null,
+ modifier = Modifier.padding(end = 8.dp)
+ )
+ Text(stringResource(R.string.susfs_reset_to_default))
+ }
+ }
+ },
+ confirmButton = {
+ Row(
+ horizontalArrangement = Arrangement.spacedBy(8.dp)
+ ) {
+ TextButton(
+ onClick = onDismiss,
+ enabled = !isLoading
+ ) {
+ Text(stringResource(R.string.cancel))
+ }
+
+ Button(
+ onClick = {
+ if (unameValue.isNotBlank()) {
+ coroutineScope.launch {
+ isLoading = true
+ val success = SuSFSManager.setUname(context, unameValue.trim())
+ if (success) {
+ lastAppliedValue = unameValue.trim()
+ onDismiss()
+ }
+ isLoading = false
+ }
+ }
+ },
+ enabled = !isLoading && unameValue.isNotBlank()
+ ) {
+ Text(
+ stringResource(R.string.susfs_apply)
+ )
+ }
+ }
+ },
+ dismissButton = null
+ )
+}
\ No newline at end of file
diff --git a/manager/app/src/main/java/com/sukisu/ultra/ui/util/SuSFSManager.kt b/manager/app/src/main/java/com/sukisu/ultra/ui/util/SuSFSManager.kt
new file mode 100644
index 00000000..a829bfa3
--- /dev/null
+++ b/manager/app/src/main/java/com/sukisu/ultra/ui/util/SuSFSManager.kt
@@ -0,0 +1,399 @@
+package com.sukisu.ultra.ui.util
+
+import android.content.Context
+import android.content.SharedPreferences
+import android.widget.Toast
+import com.sukisu.ultra.R
+import com.topjohnwu.superuser.Shell
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.withContext
+import java.io.FileOutputStream
+import java.io.IOException
+import java.io.File
+
+/**
+ * SuSFS 配置管理器
+ * 用于管理SuSFS相关的配置和命令执行
+ */
+object SuSFSManager {
+ private const val PREFS_NAME = "susfs_config"
+ private const val KEY_UNAME_VALUE = "uname_value"
+ private const val KEY_IS_ENABLED = "is_enabled"
+ private const val KEY_AUTO_START_ENABLED = "auto_start_enabled"
+ private const val KEY_LAST_APPLIED_VALUE = "last_applied_value"
+ private const val SUSFS_BINARY_NAME = "ksu_susfs"
+ private const val DEFAULT_UNAME = "default"
+ private const val STARTUP_SCRIPT_PATH = "/data/adb/service.d/susfs_startup.sh"
+ private const val SUSFS_TARGET_PATH = "/data/adb/ksu/bin/$SUSFS_BINARY_NAME"
+
+ /**
+ * 获取Root Shell实例
+ */
+ private fun getRootShell(): Shell {
+ return Shell.getShell()
+ }
+
+ /**
+ * 获取SuSFS配置的SharedPreferences
+ */
+ private fun getPrefs(context: Context): SharedPreferences {
+ return context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
+ }
+
+ /**
+ * 保存uname值
+ */
+ fun saveUnameValue(context: Context, value: String) {
+ getPrefs(context).edit().apply {
+ putString(KEY_UNAME_VALUE, value)
+ apply()
+ }
+ }
+
+ /**
+ * 获取保存的uname值
+ */
+ fun getUnameValue(context: Context): String {
+ return getPrefs(context).getString(KEY_UNAME_VALUE, DEFAULT_UNAME) ?: DEFAULT_UNAME
+ }
+
+ /**
+ * 保存最后应用的值
+ */
+ private fun saveLastAppliedValue(context: Context, value: String) {
+ getPrefs(context).edit().apply {
+ putString(KEY_LAST_APPLIED_VALUE, value)
+ apply()
+ }
+ }
+
+ /**
+ * 获取最后应用的值
+ */
+ fun getLastAppliedValue(context: Context): String {
+ return getPrefs(context).getString(KEY_LAST_APPLIED_VALUE, DEFAULT_UNAME) ?: DEFAULT_UNAME
+ }
+
+ /**
+ * 保存SuSFS启用状态
+ */
+ fun setEnabled(context: Context, enabled: Boolean) {
+ getPrefs(context).edit().apply {
+ putBoolean(KEY_IS_ENABLED, enabled)
+ apply()
+ }
+ }
+
+ /**
+ * 设置开机自启动状态
+ */
+ fun setAutoStartEnabled(context: Context, enabled: Boolean) {
+ getPrefs(context).edit().apply {
+ putBoolean(KEY_AUTO_START_ENABLED, enabled)
+ apply()
+ }
+ }
+
+ /**
+ * 获取开机自启动状态
+ */
+ fun isAutoStartEnabled(context: Context): Boolean {
+ return getPrefs(context).getBoolean(KEY_AUTO_START_ENABLED, false)
+ }
+
+ /**
+ * 从assets复制ksu_susfs文件到/data/adb/ksu/bin/
+ */
+ private suspend fun copyBinaryFromAssets(context: Context): String? = withContext(Dispatchers.IO) {
+ try {
+ val inputStream = context.assets.open(SUSFS_BINARY_NAME)
+ val tempFile = File(context.cacheDir, SUSFS_BINARY_NAME)
+
+ FileOutputStream(tempFile).use { outputStream ->
+ inputStream.copyTo(outputStream)
+ }
+
+ // 创建目标目录并复制文件到/data/adb/ksu/bin/
+ val shell = getRootShell()
+ val commands = arrayOf(
+ "cp '${tempFile.absolutePath}' '$SUSFS_TARGET_PATH'",
+ "chmod 755 '$SUSFS_TARGET_PATH'",
+ )
+
+ var success = true
+ for (command in commands) {
+ val result = shell.newJob().add(command).exec()
+ if (!result.isSuccess) {
+ success = false
+ break
+ }
+ }
+
+ // 清理临时文件
+ tempFile.delete()
+
+ if (success) {
+ val verifyResult = shell.newJob().add("test -f '$SUSFS_TARGET_PATH'").exec()
+ if (verifyResult.isSuccess) {
+ SUSFS_TARGET_PATH
+ } else {
+ null
+ }
+ } else {
+ null
+ }
+ } catch (e: IOException) {
+ e.printStackTrace()
+ null
+ }
+ }
+
+ /**
+ * 创建开机自启动脚本
+ */
+ private suspend fun createStartupScript(unameValue: String): Boolean = withContext(Dispatchers.IO) {
+ try {
+ val scriptContent = """#!/system/bin/sh
+# SuSFS 开机自启动脚本
+# 由 KernelSU Manager 自动生成
+
+# 等待系统完全启动
+sleep 30
+
+# 检查二进制文件是否存在
+if [ -f "$SUSFS_TARGET_PATH" ]; then
+ # 执行 SuSFS setUname 命令
+ $SUSFS_TARGET_PATH set_uname '$unameValue' '$DEFAULT_UNAME'
+
+ # 记录日志
+ echo "\$(date): SuSFS setUname executed with value: $unameValue" >> /data/adb/ksu/log/susfs_startup.log
+else
+ echo "\$(date): SuSFS binary not found at $SUSFS_TARGET_PATH" >> /data/adb/ksu/log/susfs_startup.log
+fi
+"""
+
+ val shell = getRootShell()
+ val commands = arrayOf(
+ "mkdir -p /data/adb/service.d",
+ "cat > $STARTUP_SCRIPT_PATH << 'EOF'\n$scriptContent\nEOF",
+ "chmod 755 $STARTUP_SCRIPT_PATH"
+ )
+
+ var success = true
+ for (command in commands) {
+ val result = shell.newJob().add(command).exec()
+ if (!result.isSuccess) {
+ success = false
+ break
+ }
+ }
+
+ success
+ } catch (e: Exception) {
+ e.printStackTrace()
+ false
+ }
+ }
+
+ /**
+ * 删除开机自启动脚本
+ */
+ private suspend fun removeStartupScript(): Boolean = withContext(Dispatchers.IO) {
+ try {
+ val shell = getRootShell()
+ val result = shell.newJob().add("rm -f $STARTUP_SCRIPT_PATH").exec()
+ result.isSuccess
+ } catch (e: Exception) {
+ e.printStackTrace()
+ false
+ }
+ }
+
+ /**
+ * 执行SuSFS命令设置uname
+ */
+ suspend fun setUname(context: Context, unameValue: String): Boolean = withContext(Dispatchers.IO) {
+ try {
+ // 首先复制二进制文件到/data/adb/ksu/bin/
+ val binaryPath = copyBinaryFromAssets(context)
+ if (binaryPath == null) {
+ withContext(Dispatchers.Main) {
+ Toast.makeText(
+ context,
+ context.getString(R.string.susfs_binary_not_found),
+ Toast.LENGTH_SHORT
+ ).show()
+ }
+ return@withContext false
+ }
+
+ // 构建命令
+ val command = "$binaryPath set_uname '$unameValue' '$DEFAULT_UNAME'"
+
+ // 执行命令
+ val result = getRootShell().newJob().add(command).exec()
+
+ if (result.isSuccess) {
+ // 保存配置
+ saveUnameValue(context, unameValue)
+ saveLastAppliedValue(context, unameValue)
+ setEnabled(context, true)
+
+ // 如果开启了开机自启动,更新启动脚本
+ if (isAutoStartEnabled(context)) {
+ createStartupScript(unameValue)
+ }
+
+ withContext(Dispatchers.Main) {
+ Toast.makeText(
+ context,
+ context.getString(R.string.susfs_uname_set_success, unameValue),
+ Toast.LENGTH_SHORT
+ ).show()
+ }
+ true
+ } else {
+ withContext(Dispatchers.Main) {
+ val errorOutput = result.out.joinToString("\n") + "\n" + result.err.joinToString("\n")
+ Toast.makeText(
+ context,
+ context.getString(R.string.susfs_command_failed) + "\n$errorOutput",
+ Toast.LENGTH_LONG
+ ).show()
+ }
+ false
+ }
+ } catch (e: Exception) {
+ e.printStackTrace()
+ withContext(Dispatchers.Main) {
+ Toast.makeText(
+ context,
+ context.getString(R.string.susfs_command_error, e.message ?: "Unknown error"),
+ Toast.LENGTH_SHORT
+ ).show()
+ }
+ false
+ }
+ }
+
+ /**
+ * 配置开机自启动
+ */
+ suspend fun configureAutoStart(context: Context, enabled: Boolean): Boolean = withContext(Dispatchers.IO) {
+ try {
+ if (enabled) {
+ // 启用开机自启动
+ val lastValue = getLastAppliedValue(context)
+ if (lastValue == DEFAULT_UNAME) {
+ withContext(Dispatchers.Main) {
+ Toast.makeText(
+ context,
+ context.getString(R.string.susfs_no_config_to_autostart),
+ Toast.LENGTH_SHORT
+ ).show()
+ }
+ return@withContext false
+ }
+
+ // 确保二进制文件存在于目标位置
+ val shell = getRootShell()
+ val checkResult = shell.newJob().add("test -f '$SUSFS_TARGET_PATH'").exec()
+
+ if (!checkResult.isSuccess) {
+ // 如果不存在,尝试复制
+ val binaryPath = copyBinaryFromAssets(context)
+ if (binaryPath == null) {
+ withContext(Dispatchers.Main) {
+ Toast.makeText(
+ context,
+ context.getString(R.string.susfs_binary_not_found),
+ Toast.LENGTH_SHORT
+ ).show()
+ }
+ return@withContext false
+ }
+ }
+
+ val success = createStartupScript(lastValue)
+ if (success) {
+ setAutoStartEnabled(context, true)
+ withContext(Dispatchers.Main) {
+ Toast.makeText(
+ context,
+ context.getString(R.string.susfs_autostart_enabled),
+ Toast.LENGTH_SHORT
+ ).show()
+ }
+ } else {
+ withContext(Dispatchers.Main) {
+ Toast.makeText(
+ context,
+ context.getString(R.string.susfs_autostart_enable_failed),
+ Toast.LENGTH_SHORT
+ ).show()
+ }
+ }
+ success
+ } else {
+ // 禁用开机自启动
+ val success = removeStartupScript()
+ if (success) {
+ setAutoStartEnabled(context, false)
+ withContext(Dispatchers.Main) {
+ Toast.makeText(
+ context,
+ context.getString(R.string.susfs_autostart_disabled),
+ Toast.LENGTH_SHORT
+ ).show()
+ }
+ } else {
+ withContext(Dispatchers.Main) {
+ Toast.makeText(
+ context,
+ context.getString(R.string.susfs_autostart_disable_failed),
+ Toast.LENGTH_SHORT
+ ).show()
+ }
+ }
+ success
+ }
+ } catch (e: Exception) {
+ e.printStackTrace()
+ withContext(Dispatchers.Main) {
+ Toast.makeText(
+ context,
+ context.getString(R.string.susfs_autostart_error, e.message ?: "Unknown error"),
+ Toast.LENGTH_SHORT
+ ).show()
+ }
+ false
+ }
+ }
+
+ /**
+ * 重置为默认值
+ */
+ suspend fun resetToDefault(context: Context): Boolean {
+ val success = setUname(context, DEFAULT_UNAME)
+ if (success) {
+ // 重置时清除最后应用的值
+ saveLastAppliedValue(context, DEFAULT_UNAME)
+ // 如果开启了开机自启动,需要禁用它
+ if (isAutoStartEnabled(context)) {
+ configureAutoStart(context, false)
+ }
+ }
+ return success
+ }
+
+ /**
+ * 检查ksu_susfs文件是否存在于assets中
+ */
+ fun isBinaryAvailable(context: Context): Boolean {
+ return try {
+ context.assets.open(SUSFS_BINARY_NAME).use { true }
+ } catch (_: IOException) {
+ false
+ }
+ }
+}
\ No newline at end of file
diff --git a/manager/app/src/main/java/zako/zako/zako/zakoui/screen/MoreSettings.kt b/manager/app/src/main/java/zako/zako/zako/zakoui/screen/MoreSettings.kt
index 49ba9eac..0ef4b406 100644
--- a/manager/app/src/main/java/zako/zako/zako/zakoui/screen/MoreSettings.kt
+++ b/manager/app/src/main/java/zako/zako/zako/zakoui/screen/MoreSettings.kt
@@ -63,6 +63,7 @@ import com.sukisu.ultra.Natives
import com.sukisu.ultra.R
import com.sukisu.ultra.ui.component.ImageEditorDialog
import com.sukisu.ultra.ui.component.KsuIsValid
+import com.sukisu.ultra.ui.component.SuSFSConfigDialog
import com.sukisu.ultra.ui.theme.CardConfig.cardElevation
import com.sukisu.ultra.ui.theme.*
import com.sukisu.ultra.ui.util.*
@@ -145,6 +146,7 @@ fun MoreSettingsScreen(
var showThemeColorDialog by remember { mutableStateOf(false) }
var showDpiConfirmDialog by remember { mutableStateOf(false) }
var showImageEditor by remember { mutableStateOf(false) }
+ var showSuSFSConfigDialog by remember { mutableStateOf(false) }
// 主题模式选项
val themeOptions = listOf(
@@ -475,6 +477,13 @@ fun MoreSettingsScreen(
)
}
+ // SuSFS配置对话框
+ if (showSuSFSConfigDialog) {
+ SuSFSConfigDialog(
+ onDismiss = { showSuSFSConfigDialog = false }
+ )
+ }
+
// 主题模式选择对话框
if (showThemeModeDialog) {
SingleChoiceDialog(
@@ -1120,7 +1129,20 @@ fun MoreSettingsScreen(
)
}
- // SuSFS 配置(仅在支持时显示)
+ // SuSFS 配置(仅在支持时显示存)
+ if (getSuSFS() == "Supported" && SuSFSManager.isBinaryAvailable(context)) {
+ SettingItem(
+ icon = Icons.Default.Settings,
+ title = stringResource(R.string.susfs_config_setting_title),
+ subtitle = stringResource(
+ R.string.susfs_config_setting_summary,
+ SuSFSManager.getUnameValue(context)
+ ),
+ onClick = { showSuSFSConfigDialog = true }
+ )
+ }
+
+ // SuSFS 开关(仅在支持时显示)
val suSFS = getSuSFS()
val isSUS_SU = getSuSFSFeatures()
if (suSFS == "Supported" && isSUS_SU == "CONFIG_KSU_SUSFS_SUS_SU") {
diff --git a/manager/app/src/main/res/values-zh-rCN/strings.xml b/manager/app/src/main/res/values-zh-rCN/strings.xml
index 42174cb5..07fff863 100644
--- a/manager/app/src/main/res/values-zh-rCN/strings.xml
+++ b/manager/app/src/main/res/values-zh-rCN/strings.xml
@@ -396,4 +396,35 @@
菜单选项
排序方式
应用类型选择
+
+ SuSFS 配置
+ 配置说明
+ 此功能允许您自定义 SuSFS 的 uname 值。输入您想要设置的值,点击应用即可生效
+ Uname 值
+ 请输入自定义 uname 值
+ 当前值: %s
+ 重置为默认值
+ 应用
+
+ 确认重置
+ 确定要将 SuSFS uname 重置为默认值吗?此操作不可撤销。
+ 确认重置
+
+ 无法找到 ksu_susfs 文件
+ SuSFS 命令执行失败
+ 执行 SuSFS 命令时出错: %s
+ SuSFS uname 设置成功: %s
+
+ SuSFS 配置
+ 配置 SuSFS 的 uname 值 (当前: %s)
+
+ 开机自启动
+ 系统启动时自动应用 uname 配置
+ 开机自启动已启用
+ 开机自启动已禁用
+ 启用开机自启动失败
+ 禁用开机自启动失败
+ 开机自启动配置错误: %s
+ 没有可用的配置进行开机自启动
+ 需要输入非默认值才能启用开机自启动
diff --git a/manager/app/src/main/res/values/strings.xml b/manager/app/src/main/res/values/strings.xml
index 6eb6af32..8dd1a4cc 100644
--- a/manager/app/src/main/res/values/strings.xml
+++ b/manager/app/src/main/res/values/strings.xml
@@ -398,4 +398,35 @@
Menu Options
Sort by
Application Type Selection
+
+ SuSFS Configuration
+ Configuration Description
+ This feature allows you to customize the SuSFS uname value. Enter the value you want to set and click Apply to take effect.
+ Uname Value
+ Please enter custom uname value
+ Current value: %s
+ Reset to Default
+ Apply
+
+ Confirm Reset
+ Are you sure you want to reset SuSFS uname to default value? This action cannot be undone.
+ Confirm Reset
+
+ Cannot find ksu_susfs file
+ SuSFS command execution failed
+ Error executing SuSFS command: %s
+ SuSFS uname set successfully: %s
+
+ SuSFS Configuration
+ Configure SuSFS uname value (Current: %s)
+
+ boot-up
+ Automatic application of uname configuration at system startup
+ Boot Self-Start is enabled
+ Boot Self-Start is disabled
+ Failed to enable boot self-start
+ Failure to disable boot-up
+ Boot-up misconfiguration: %s
+ There is no available configuration for boot self-start
+ Need to enter a non-default value to enable bootstrapping