manager: Add configure susfs uname value in more settings
Signed-off-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
This commit is contained in:
BIN
manager/app/src/main/assets/ksu_susfs
Normal file
BIN
manager/app/src/main/assets/ksu_susfs
Normal file
Binary file not shown.
@@ -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
|
||||
)
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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") {
|
||||
|
||||
@@ -396,4 +396,35 @@
|
||||
<string name="menu_options">菜单选项</string>
|
||||
<string name="sort_options">排序方式</string>
|
||||
<string name="app_categories">应用类型选择</string>
|
||||
<!-- SuSFS Configuration -->
|
||||
<string name="susfs_config_title">SuSFS 配置</string>
|
||||
<string name="susfs_config_description">配置说明</string>
|
||||
<string name="susfs_config_description_text">此功能允许您自定义 SuSFS 的 uname 值。输入您想要设置的值,点击应用即可生效</string>
|
||||
<string name="susfs_uname_label">Uname 值</string>
|
||||
<string name="susfs_uname_placeholder">请输入自定义 uname 值</string>
|
||||
<string name="susfs_current_value">当前值: %s</string>
|
||||
<string name="susfs_reset_to_default">重置为默认值</string>
|
||||
<string name="susfs_apply">应用</string>
|
||||
<!-- SuSFS Reset Confirmation -->
|
||||
<string name="susfs_reset_confirm_title">确认重置</string>
|
||||
<string name="susfs_reset_confirm_message">确定要将 SuSFS uname 重置为默认值吗?此操作不可撤销。</string>
|
||||
<string name="susfs_reset_confirm">确认重置</string>
|
||||
<!-- SuSFS Toast Messages -->
|
||||
<string name="susfs_binary_not_found">无法找到 ksu_susfs 文件</string>
|
||||
<string name="susfs_command_failed">SuSFS 命令执行失败</string>
|
||||
<string name="susfs_command_error">执行 SuSFS 命令时出错: %s</string>
|
||||
<string name="susfs_uname_set_success">SuSFS uname 设置成功: %s</string>
|
||||
<!-- SuSFS Settings Item -->
|
||||
<string name="susfs_config_setting_title">SuSFS 配置</string>
|
||||
<string name="susfs_config_setting_summary">配置 SuSFS 的 uname 值 (当前: %s)</string>
|
||||
<!-- 开机自启动相关 -->
|
||||
<string name="susfs_autostart_title">开机自启动</string>
|
||||
<string name="susfs_autostart_description">系统启动时自动应用 uname 配置</string>
|
||||
<string name="susfs_autostart_enabled">开机自启动已启用</string>
|
||||
<string name="susfs_autostart_disabled">开机自启动已禁用</string>
|
||||
<string name="susfs_autostart_enable_failed">启用开机自启动失败</string>
|
||||
<string name="susfs_autostart_disable_failed">禁用开机自启动失败</string>
|
||||
<string name="susfs_autostart_error">开机自启动配置错误: %s</string>
|
||||
<string name="susfs_no_config_to_autostart">没有可用的配置进行开机自启动</string>
|
||||
<string name="susfs_autostart_tis">需要输入非默认值才能启用开机自启动</string>
|
||||
</resources>
|
||||
|
||||
@@ -398,4 +398,35 @@
|
||||
<string name="menu_options">Menu Options</string>
|
||||
<string name="sort_options">Sort by</string>
|
||||
<string name="app_categories">Application Type Selection</string>
|
||||
<!-- SuSFS Configuration -->
|
||||
<string name="susfs_config_title">SuSFS Configuration</string>
|
||||
<string name="susfs_config_description">Configuration Description</string>
|
||||
<string name="susfs_config_description_text">This feature allows you to customize the SuSFS uname value. Enter the value you want to set and click Apply to take effect.</string>
|
||||
<string name="susfs_uname_label">Uname Value</string>
|
||||
<string name="susfs_uname_placeholder">Please enter custom uname value</string>
|
||||
<string name="susfs_current_value">Current value: %s</string>
|
||||
<string name="susfs_reset_to_default">Reset to Default</string>
|
||||
<string name="susfs_apply">Apply</string>
|
||||
<!-- SuSFS Reset Confirmation -->
|
||||
<string name="susfs_reset_confirm_title">Confirm Reset</string>
|
||||
<string name="susfs_reset_confirm_message">Are you sure you want to reset SuSFS uname to default value? This action cannot be undone.</string>
|
||||
<string name="susfs_reset_confirm">Confirm Reset</string>
|
||||
<!-- SuSFS Toast Messages -->
|
||||
<string name="susfs_binary_not_found">Cannot find ksu_susfs file</string>
|
||||
<string name="susfs_command_failed">SuSFS command execution failed</string>
|
||||
<string name="susfs_command_error">Error executing SuSFS command: %s</string>
|
||||
<string name="susfs_uname_set_success">SuSFS uname set successfully: %s</string>
|
||||
<!-- SuSFS Settings Item -->
|
||||
<string name="susfs_config_setting_title">SuSFS Configuration</string>
|
||||
<string name="susfs_config_setting_summary">Configure SuSFS uname value (Current: %s)</string>
|
||||
<!-- 开机自启动相关 -->
|
||||
<string name="susfs_autostart_title">boot-up</string>
|
||||
<string name="susfs_autostart_description">Automatic application of uname configuration at system startup</string>
|
||||
<string name="susfs_autostart_enabled">Boot Self-Start is enabled</string>
|
||||
<string name="susfs_autostart_disabled">Boot Self-Start is disabled</string>
|
||||
<string name="susfs_autostart_enable_failed">Failed to enable boot self-start</string>
|
||||
<string name="susfs_autostart_disable_failed">Failure to disable boot-up</string>
|
||||
<string name="susfs_autostart_error">Boot-up misconfiguration: %s</string>
|
||||
<string name="susfs_no_config_to_autostart">There is no available configuration for boot self-start</string>
|
||||
<string name="susfs_autostart_tis">Need to enter a non-default value to enable bootstrapping</string>
|
||||
</resources>
|
||||
|
||||
Reference in New Issue
Block a user