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