manager: Add SuSFS Kstat-related configuration
This commit is contained in:
@@ -82,6 +82,7 @@ import com.sukisu.ultra.ui.screen.extensions.FeatureStatusCard
|
|||||||
import com.sukisu.ultra.ui.screen.extensions.SusPathsContent
|
import com.sukisu.ultra.ui.screen.extensions.SusPathsContent
|
||||||
import com.sukisu.ultra.ui.screen.extensions.SusMountsContent
|
import com.sukisu.ultra.ui.screen.extensions.SusMountsContent
|
||||||
import com.sukisu.ultra.ui.screen.extensions.TryUmountContent
|
import com.sukisu.ultra.ui.screen.extensions.TryUmountContent
|
||||||
|
import com.sukisu.ultra.ui.screen.extensions.KstatConfigContent
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -92,6 +93,7 @@ enum class SuSFSTab(val displayNameRes: Int) {
|
|||||||
SUS_PATHS(R.string.susfs_tab_sus_paths),
|
SUS_PATHS(R.string.susfs_tab_sus_paths),
|
||||||
SUS_MOUNTS(R.string.susfs_tab_sus_mounts),
|
SUS_MOUNTS(R.string.susfs_tab_sus_mounts),
|
||||||
TRY_UMOUNT(R.string.susfs_tab_try_umount),
|
TRY_UMOUNT(R.string.susfs_tab_try_umount),
|
||||||
|
KSTAT_CONFIG(R.string.susfs_tab_kstat_config),
|
||||||
PATH_SETTINGS(R.string.susfs_tab_path_settings),
|
PATH_SETTINGS(R.string.susfs_tab_path_settings),
|
||||||
ENABLED_FEATURES(R.string.susfs_tab_enabled_features);
|
ENABLED_FEATURES(R.string.susfs_tab_enabled_features);
|
||||||
|
|
||||||
@@ -136,6 +138,10 @@ fun SuSFSConfigScreen(
|
|||||||
var androidDataPath by remember { mutableStateOf("") }
|
var androidDataPath by remember { mutableStateOf("") }
|
||||||
var sdcardPath by remember { mutableStateOf("") }
|
var sdcardPath by remember { mutableStateOf("") }
|
||||||
|
|
||||||
|
// Kstat配置相关状态
|
||||||
|
var kstatConfigs by remember { mutableStateOf(emptySet<String>()) }
|
||||||
|
var addKstatPaths by remember { mutableStateOf(emptySet<String>()) }
|
||||||
|
|
||||||
// 启用功能状态相关
|
// 启用功能状态相关
|
||||||
var enabledFeatures by remember { mutableStateOf(emptyList<SuSFSManager.EnabledFeature>()) }
|
var enabledFeatures by remember { mutableStateOf(emptyList<SuSFSManager.EnabledFeature>()) }
|
||||||
var isLoadingFeatures by remember { mutableStateOf(false) }
|
var isLoadingFeatures by remember { mutableStateOf(false) }
|
||||||
@@ -151,14 +157,32 @@ fun SuSFSConfigScreen(
|
|||||||
var newUmountMode by remember { mutableIntStateOf(0) }
|
var newUmountMode by remember { mutableIntStateOf(0) }
|
||||||
var umountModeExpanded by remember { mutableStateOf(false) }
|
var umountModeExpanded by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
|
// Kstat配置对话框状态
|
||||||
|
var showAddKstatStaticallyDialog by remember { mutableStateOf(false) }
|
||||||
|
var showAddKstatDialog by remember { mutableStateOf(false) }
|
||||||
|
var newKstatPath by remember { mutableStateOf("") }
|
||||||
|
var newKstatIno by remember { mutableStateOf("") }
|
||||||
|
var newKstatDev by remember { mutableStateOf("") }
|
||||||
|
var newKstatNlink by remember { mutableStateOf("") }
|
||||||
|
var newKstatSize by remember { mutableStateOf("") }
|
||||||
|
var newKstatAtime by remember { mutableStateOf("") }
|
||||||
|
var newKstatAtimeNsec by remember { mutableStateOf("") }
|
||||||
|
var newKstatMtime by remember { mutableStateOf("") }
|
||||||
|
var newKstatMtimeNsec by remember { mutableStateOf("") }
|
||||||
|
var newKstatCtime by remember { mutableStateOf("") }
|
||||||
|
var newKstatCtimeNsec by remember { mutableStateOf("") }
|
||||||
|
var newKstatBlocks by remember { mutableStateOf("") }
|
||||||
|
var newKstatBlksize by remember { mutableStateOf("") }
|
||||||
|
|
||||||
// 重置确认对话框状态
|
// 重置确认对话框状态
|
||||||
var showResetPathsDialog by remember { mutableStateOf(false) }
|
var showResetPathsDialog by remember { mutableStateOf(false) }
|
||||||
var showResetMountsDialog by remember { mutableStateOf(false) }
|
var showResetMountsDialog by remember { mutableStateOf(false) }
|
||||||
var showResetUmountsDialog by remember { mutableStateOf(false) }
|
var showResetUmountsDialog by remember { mutableStateOf(false) }
|
||||||
|
var showResetKstatDialog by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
val allTabs = SuSFSTab.getAllTabs()
|
val allTabs = SuSFSTab.getAllTabs()
|
||||||
|
|
||||||
// 实时判断是否可以启用开机自启动 - 修改逻辑
|
// 实时判断是否可以启用开机自启动
|
||||||
val canEnableAutoStart by remember {
|
val canEnableAutoStart by remember {
|
||||||
derivedStateOf {
|
derivedStateOf {
|
||||||
SuSFSManager.hasConfigurationForAutoStart(context)
|
SuSFSManager.hasConfigurationForAutoStart(context)
|
||||||
@@ -197,6 +221,8 @@ fun SuSFSConfigScreen(
|
|||||||
tryUmounts = SuSFSManager.getTryUmounts(context)
|
tryUmounts = SuSFSManager.getTryUmounts(context)
|
||||||
androidDataPath = SuSFSManager.getAndroidDataPath(context)
|
androidDataPath = SuSFSManager.getAndroidDataPath(context)
|
||||||
sdcardPath = SuSFSManager.getSdcardPath(context)
|
sdcardPath = SuSFSManager.getSdcardPath(context)
|
||||||
|
kstatConfigs = SuSFSManager.getKstatConfigs(context)
|
||||||
|
addKstatPaths = SuSFSManager.getAddKstatPaths(context)
|
||||||
|
|
||||||
// 加载槽位信息
|
// 加载槽位信息
|
||||||
loadSlotInfo()
|
loadSlotInfo()
|
||||||
@@ -581,6 +607,313 @@ fun SuSFSConfigScreen(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 添加Kstat静态配置对话框
|
||||||
|
if (showAddKstatStaticallyDialog) {
|
||||||
|
AlertDialog(
|
||||||
|
onDismissRequest = { showAddKstatStaticallyDialog = false },
|
||||||
|
title = {
|
||||||
|
Text(
|
||||||
|
stringResource(R.string.add_kstat_statically_title),
|
||||||
|
style = MaterialTheme.typography.titleLarge,
|
||||||
|
fontWeight = FontWeight.Bold
|
||||||
|
)
|
||||||
|
},
|
||||||
|
text = {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.verticalScroll(rememberScrollState()),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
|
) {
|
||||||
|
OutlinedTextField(
|
||||||
|
value = newKstatPath,
|
||||||
|
onValueChange = { newKstatPath = it },
|
||||||
|
label = { Text(stringResource(R.string.file_or_directory_path_label)) },
|
||||||
|
placeholder = { Text("/path/to/file_or_directory") },
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
shape = RoundedCornerShape(8.dp)
|
||||||
|
)
|
||||||
|
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
|
) {
|
||||||
|
OutlinedTextField(
|
||||||
|
value = newKstatIno,
|
||||||
|
onValueChange = { newKstatIno = it },
|
||||||
|
label = { Text("ino") },
|
||||||
|
placeholder = { Text("1234") },
|
||||||
|
modifier = Modifier.weight(1f),
|
||||||
|
shape = RoundedCornerShape(8.dp)
|
||||||
|
)
|
||||||
|
OutlinedTextField(
|
||||||
|
value = newKstatDev,
|
||||||
|
onValueChange = { newKstatDev = it },
|
||||||
|
label = { Text("dev") },
|
||||||
|
placeholder = { Text("1234") },
|
||||||
|
modifier = Modifier.weight(1f),
|
||||||
|
shape = RoundedCornerShape(8.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
|
) {
|
||||||
|
OutlinedTextField(
|
||||||
|
value = newKstatNlink,
|
||||||
|
onValueChange = { newKstatNlink = it },
|
||||||
|
label = { Text("nlink") },
|
||||||
|
placeholder = { Text("2") },
|
||||||
|
modifier = Modifier.weight(1f),
|
||||||
|
shape = RoundedCornerShape(8.dp)
|
||||||
|
)
|
||||||
|
OutlinedTextField(
|
||||||
|
value = newKstatSize,
|
||||||
|
onValueChange = { newKstatSize = it },
|
||||||
|
label = { Text("size") },
|
||||||
|
placeholder = { Text("223344") },
|
||||||
|
modifier = Modifier.weight(1f),
|
||||||
|
shape = RoundedCornerShape(8.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
|
) {
|
||||||
|
OutlinedTextField(
|
||||||
|
value = newKstatAtime,
|
||||||
|
onValueChange = { newKstatAtime = it },
|
||||||
|
label = { Text("atime") },
|
||||||
|
placeholder = { Text("1712592355") },
|
||||||
|
modifier = Modifier.weight(1f),
|
||||||
|
shape = RoundedCornerShape(8.dp)
|
||||||
|
)
|
||||||
|
OutlinedTextField(
|
||||||
|
value = newKstatAtimeNsec,
|
||||||
|
onValueChange = { newKstatAtimeNsec = it },
|
||||||
|
label = { Text("atime_nsec") },
|
||||||
|
placeholder = { Text("0") },
|
||||||
|
modifier = Modifier.weight(1f),
|
||||||
|
shape = RoundedCornerShape(8.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
|
) {
|
||||||
|
OutlinedTextField(
|
||||||
|
value = newKstatMtime,
|
||||||
|
onValueChange = { newKstatMtime = it },
|
||||||
|
label = { Text("mtime") },
|
||||||
|
placeholder = { Text("1712592355") },
|
||||||
|
modifier = Modifier.weight(1f),
|
||||||
|
shape = RoundedCornerShape(8.dp)
|
||||||
|
)
|
||||||
|
OutlinedTextField(
|
||||||
|
value = newKstatMtimeNsec,
|
||||||
|
onValueChange = { newKstatMtimeNsec = it },
|
||||||
|
label = { Text("mtime_nsec") },
|
||||||
|
placeholder = { Text("0") },
|
||||||
|
modifier = Modifier.weight(1f),
|
||||||
|
shape = RoundedCornerShape(8.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
|
) {
|
||||||
|
OutlinedTextField(
|
||||||
|
value = newKstatCtime,
|
||||||
|
onValueChange = { newKstatCtime = it },
|
||||||
|
label = { Text("ctime") },
|
||||||
|
placeholder = { Text("1712592355") },
|
||||||
|
modifier = Modifier.weight(1f),
|
||||||
|
shape = RoundedCornerShape(8.dp)
|
||||||
|
)
|
||||||
|
OutlinedTextField(
|
||||||
|
value = newKstatCtimeNsec,
|
||||||
|
onValueChange = { newKstatCtimeNsec = it },
|
||||||
|
label = { Text("ctime_nsec") },
|
||||||
|
placeholder = { Text("0") },
|
||||||
|
modifier = Modifier.weight(1f),
|
||||||
|
shape = RoundedCornerShape(8.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
|
) {
|
||||||
|
OutlinedTextField(
|
||||||
|
value = newKstatBlocks,
|
||||||
|
onValueChange = { newKstatBlocks = it },
|
||||||
|
label = { Text("blocks") },
|
||||||
|
placeholder = { Text("16") },
|
||||||
|
modifier = Modifier.weight(1f),
|
||||||
|
shape = RoundedCornerShape(8.dp)
|
||||||
|
)
|
||||||
|
OutlinedTextField(
|
||||||
|
value = newKstatBlksize,
|
||||||
|
onValueChange = { newKstatBlksize = it },
|
||||||
|
label = { Text("blksize") },
|
||||||
|
placeholder = { Text("512") },
|
||||||
|
modifier = Modifier.weight(1f),
|
||||||
|
shape = RoundedCornerShape(8.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.hint_use_default_value),
|
||||||
|
style = MaterialTheme.typography.bodySmall,
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
confirmButton = {
|
||||||
|
Button(
|
||||||
|
onClick = {
|
||||||
|
if (newKstatPath.isNotBlank()) {
|
||||||
|
coroutineScope.launch {
|
||||||
|
isLoading = true
|
||||||
|
if (SuSFSManager.addKstatStatically(
|
||||||
|
context, newKstatPath.trim(),
|
||||||
|
newKstatIno.trim().ifBlank { "default" },
|
||||||
|
newKstatDev.trim().ifBlank { "default" },
|
||||||
|
newKstatNlink.trim().ifBlank { "default" },
|
||||||
|
newKstatSize.trim().ifBlank { "default" },
|
||||||
|
newKstatAtime.trim().ifBlank { "default" },
|
||||||
|
newKstatAtimeNsec.trim().ifBlank { "default" },
|
||||||
|
newKstatMtime.trim().ifBlank { "default" },
|
||||||
|
newKstatMtimeNsec.trim().ifBlank { "default" },
|
||||||
|
newKstatCtime.trim().ifBlank { "default" },
|
||||||
|
newKstatCtimeNsec.trim().ifBlank { "default" },
|
||||||
|
newKstatBlocks.trim().ifBlank { "default" },
|
||||||
|
newKstatBlksize.trim().ifBlank { "default" }
|
||||||
|
)) {
|
||||||
|
kstatConfigs = SuSFSManager.getKstatConfigs(context)
|
||||||
|
}
|
||||||
|
isLoading = false
|
||||||
|
// 清空所有字段
|
||||||
|
newKstatPath = ""
|
||||||
|
newKstatIno = ""
|
||||||
|
newKstatDev = ""
|
||||||
|
newKstatNlink = ""
|
||||||
|
newKstatSize = ""
|
||||||
|
newKstatAtime = ""
|
||||||
|
newKstatAtimeNsec = ""
|
||||||
|
newKstatMtime = ""
|
||||||
|
newKstatMtimeNsec = ""
|
||||||
|
newKstatCtime = ""
|
||||||
|
newKstatCtimeNsec = ""
|
||||||
|
newKstatBlocks = ""
|
||||||
|
newKstatBlksize = ""
|
||||||
|
showAddKstatStaticallyDialog = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
enabled = newKstatPath.isNotBlank() && !isLoading,
|
||||||
|
shape = RoundedCornerShape(8.dp)
|
||||||
|
) {
|
||||||
|
Text("添加")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dismissButton = {
|
||||||
|
TextButton(
|
||||||
|
onClick = {
|
||||||
|
showAddKstatStaticallyDialog = false
|
||||||
|
// 清空所有字段
|
||||||
|
newKstatPath = ""
|
||||||
|
newKstatIno = ""
|
||||||
|
newKstatDev = ""
|
||||||
|
newKstatNlink = ""
|
||||||
|
newKstatSize = ""
|
||||||
|
newKstatAtime = ""
|
||||||
|
newKstatAtimeNsec = ""
|
||||||
|
newKstatMtime = ""
|
||||||
|
newKstatMtimeNsec = ""
|
||||||
|
newKstatCtime = ""
|
||||||
|
newKstatCtimeNsec = ""
|
||||||
|
newKstatBlocks = ""
|
||||||
|
newKstatBlksize = ""
|
||||||
|
},
|
||||||
|
shape = RoundedCornerShape(8.dp)
|
||||||
|
) {
|
||||||
|
Text(stringResource(R.string.cancel))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
shape = RoundedCornerShape(12.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加Kstat路径对话框
|
||||||
|
if (showAddKstatDialog) {
|
||||||
|
AlertDialog(
|
||||||
|
onDismissRequest = { showAddKstatDialog = false },
|
||||||
|
title = {
|
||||||
|
Text(
|
||||||
|
stringResource(R.string.add_kstat_path_title),
|
||||||
|
style = MaterialTheme.typography.titleLarge,
|
||||||
|
fontWeight = FontWeight.Bold
|
||||||
|
)
|
||||||
|
},
|
||||||
|
text = {
|
||||||
|
Column(
|
||||||
|
verticalArrangement = Arrangement.spacedBy(12.dp)
|
||||||
|
) {
|
||||||
|
OutlinedTextField(
|
||||||
|
value = newKstatPath,
|
||||||
|
onValueChange = { newKstatPath = it },
|
||||||
|
label = { Text(stringResource(R.string.file_or_directory_path_label)) },
|
||||||
|
placeholder = { Text("/path/to/file_or_directory") },
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
shape = RoundedCornerShape(8.dp)
|
||||||
|
)
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.kstat_command_description),
|
||||||
|
style = MaterialTheme.typography.bodySmall,
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
confirmButton = {
|
||||||
|
Button(
|
||||||
|
onClick = {
|
||||||
|
if (newKstatPath.isNotBlank()) {
|
||||||
|
coroutineScope.launch {
|
||||||
|
isLoading = true
|
||||||
|
if (SuSFSManager.addKstat(context, newKstatPath.trim())) {
|
||||||
|
addKstatPaths = SuSFSManager.getAddKstatPaths(context)
|
||||||
|
}
|
||||||
|
isLoading = false
|
||||||
|
newKstatPath = ""
|
||||||
|
showAddKstatDialog = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
enabled = newKstatPath.isNotBlank() && !isLoading,
|
||||||
|
shape = RoundedCornerShape(8.dp)
|
||||||
|
) {
|
||||||
|
Text(stringResource(R.string.add))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dismissButton = {
|
||||||
|
TextButton(
|
||||||
|
onClick = {
|
||||||
|
showAddKstatDialog = false
|
||||||
|
newKstatPath = ""
|
||||||
|
},
|
||||||
|
shape = RoundedCornerShape(8.dp)
|
||||||
|
) {
|
||||||
|
Text(stringResource(R.string.cancel))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
shape = RoundedCornerShape(12.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// 运行尝试卸载确认对话框
|
// 运行尝试卸载确认对话框
|
||||||
if (showRunUmountDialog) {
|
if (showRunUmountDialog) {
|
||||||
AlertDialog(
|
AlertDialog(
|
||||||
@@ -762,6 +1095,55 @@ fun SuSFSConfigScreen(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 重置Kstat配置确认对话框
|
||||||
|
if (showResetKstatDialog) {
|
||||||
|
AlertDialog(
|
||||||
|
onDismissRequest = { showResetKstatDialog = false },
|
||||||
|
title = {
|
||||||
|
Text(
|
||||||
|
stringResource(R.string.reset_kstat_config_title),
|
||||||
|
style = MaterialTheme.typography.titleLarge,
|
||||||
|
fontWeight = FontWeight.Bold
|
||||||
|
)
|
||||||
|
},
|
||||||
|
text = { Text(stringResource(R.string.reset_kstat_config_message)) },
|
||||||
|
confirmButton = {
|
||||||
|
Button(
|
||||||
|
onClick = {
|
||||||
|
coroutineScope.launch {
|
||||||
|
isLoading = true
|
||||||
|
SuSFSManager.saveKstatConfigs(context, emptySet())
|
||||||
|
SuSFSManager.saveAddKstatPaths(context, emptySet())
|
||||||
|
kstatConfigs = emptySet()
|
||||||
|
addKstatPaths = emptySet()
|
||||||
|
if (SuSFSManager.isAutoStartEnabled(context)) {
|
||||||
|
SuSFSManager.configureAutoStart(context, true)
|
||||||
|
}
|
||||||
|
isLoading = false
|
||||||
|
showResetKstatDialog = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
enabled = !isLoading,
|
||||||
|
colors = ButtonDefaults.buttonColors(
|
||||||
|
containerColor = MaterialTheme.colorScheme.error
|
||||||
|
),
|
||||||
|
shape = RoundedCornerShape(8.dp)
|
||||||
|
) {
|
||||||
|
Text(stringResource(R.string.confirm_reset))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dismissButton = {
|
||||||
|
TextButton(
|
||||||
|
onClick = { showResetKstatDialog = false },
|
||||||
|
shape = RoundedCornerShape(8.dp)
|
||||||
|
) {
|
||||||
|
Text(stringResource(R.string.cancel))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
shape = RoundedCornerShape(12.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// 重置确认对话框
|
// 重置确认对话框
|
||||||
if (showConfirmReset) {
|
if (showConfirmReset) {
|
||||||
AlertDialog(
|
AlertDialog(
|
||||||
@@ -985,6 +1367,29 @@ fun SuSFSConfigScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SuSFSTab.KSTAT_CONFIG -> {
|
||||||
|
// 重置按钮
|
||||||
|
OutlinedButton(
|
||||||
|
onClick = { showResetKstatDialog = true },
|
||||||
|
enabled = !isLoading && (kstatConfigs.isNotEmpty() || addKstatPaths.isNotEmpty()),
|
||||||
|
shape = RoundedCornerShape(8.dp),
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.height(40.dp)
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.RestoreFromTrash,
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = Modifier.size(16.dp)
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.width(6.dp))
|
||||||
|
Text(
|
||||||
|
stringResource(R.string.reset_kstat_config_title),
|
||||||
|
fontWeight = FontWeight.Medium
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
SuSFSTab.PATH_SETTINGS -> {
|
SuSFSTab.PATH_SETTINGS -> {
|
||||||
// 重置按钮
|
// 重置按钮
|
||||||
OutlinedButton(
|
OutlinedButton(
|
||||||
@@ -1160,6 +1565,47 @@ fun SuSFSConfigScreen(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
SuSFSTab.KSTAT_CONFIG -> {
|
||||||
|
KstatConfigContent(
|
||||||
|
kstatConfigs = kstatConfigs,
|
||||||
|
addKstatPaths = addKstatPaths,
|
||||||
|
isLoading = isLoading,
|
||||||
|
onAddKstatStatically = { showAddKstatStaticallyDialog = true },
|
||||||
|
onAddKstat = { showAddKstatDialog = true },
|
||||||
|
onRemoveKstatConfig = { config ->
|
||||||
|
coroutineScope.launch {
|
||||||
|
isLoading = true
|
||||||
|
if (SuSFSManager.removeKstatConfig(context, config)) {
|
||||||
|
kstatConfigs = SuSFSManager.getKstatConfigs(context)
|
||||||
|
}
|
||||||
|
isLoading = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRemoveAddKstat = { path ->
|
||||||
|
coroutineScope.launch {
|
||||||
|
isLoading = true
|
||||||
|
if (SuSFSManager.removeAddKstat(context, path)) {
|
||||||
|
addKstatPaths = SuSFSManager.getAddKstatPaths(context)
|
||||||
|
}
|
||||||
|
isLoading = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onUpdateKstat = { path ->
|
||||||
|
coroutineScope.launch {
|
||||||
|
isLoading = true
|
||||||
|
SuSFSManager.updateKstat(context, path)
|
||||||
|
isLoading = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onUpdateKstatFullClone = { path ->
|
||||||
|
coroutineScope.launch {
|
||||||
|
isLoading = true
|
||||||
|
SuSFSManager.updateKstatFullClone(context, path)
|
||||||
|
isLoading = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
SuSFSTab.PATH_SETTINGS -> {
|
SuSFSTab.PATH_SETTINGS -> {
|
||||||
PathSettingsContent(
|
PathSettingsContent(
|
||||||
androidDataPath = androidDataPath,
|
androidDataPath = androidDataPath,
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ import androidx.compose.material.icons.filled.Delete
|
|||||||
import androidx.compose.material.icons.filled.Folder
|
import androidx.compose.material.icons.filled.Folder
|
||||||
import androidx.compose.material.icons.filled.PlayArrow
|
import androidx.compose.material.icons.filled.PlayArrow
|
||||||
import androidx.compose.material.icons.filled.Storage
|
import androidx.compose.material.icons.filled.Storage
|
||||||
|
import androidx.compose.material.icons.filled.Update
|
||||||
|
import androidx.compose.material.icons.filled.Settings
|
||||||
import androidx.compose.material3.AlertDialog
|
import androidx.compose.material3.AlertDialog
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
import androidx.compose.material3.Card
|
import androidx.compose.material3.Card
|
||||||
@@ -175,6 +177,166 @@ fun PathItemCard(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Kstat配置项目卡片组件
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
fun KstatConfigItemCard(
|
||||||
|
config: String,
|
||||||
|
onDelete: () -> Unit,
|
||||||
|
isLoading: Boolean = false
|
||||||
|
) {
|
||||||
|
Card(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(vertical = 1.dp),
|
||||||
|
shape = RoundedCornerShape(8.dp),
|
||||||
|
elevation = CardDefaults.cardElevation(defaultElevation = 1.dp)
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(12.dp),
|
||||||
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
modifier = Modifier.weight(1f)
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.Settings,
|
||||||
|
contentDescription = null,
|
||||||
|
tint = MaterialTheme.colorScheme.primary,
|
||||||
|
modifier = Modifier.size(18.dp)
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.width(12.dp))
|
||||||
|
Column {
|
||||||
|
val parts = config.split("|")
|
||||||
|
if (parts.isNotEmpty()) {
|
||||||
|
Text(
|
||||||
|
text = parts[0], // 路径
|
||||||
|
style = MaterialTheme.typography.bodyLarge,
|
||||||
|
fontWeight = FontWeight.Medium
|
||||||
|
)
|
||||||
|
if (parts.size > 1) {
|
||||||
|
Text(
|
||||||
|
text = "参数: ${parts.drop(1).joinToString(" ")}",
|
||||||
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Text(
|
||||||
|
text = config,
|
||||||
|
style = MaterialTheme.typography.bodyLarge,
|
||||||
|
fontWeight = FontWeight.Medium
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
IconButton(
|
||||||
|
onClick = onDelete,
|
||||||
|
enabled = !isLoading,
|
||||||
|
modifier = Modifier.size(32.dp)
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.Delete,
|
||||||
|
contentDescription = null,
|
||||||
|
tint = MaterialTheme.colorScheme.error,
|
||||||
|
modifier = Modifier.size(16.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add Kstat路径项目卡片组件
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
fun AddKstatPathItemCard(
|
||||||
|
path: String,
|
||||||
|
onDelete: () -> Unit,
|
||||||
|
onUpdate: () -> Unit,
|
||||||
|
onUpdateFullClone: () -> Unit,
|
||||||
|
isLoading: Boolean = false
|
||||||
|
) {
|
||||||
|
Card(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(vertical = 1.dp),
|
||||||
|
shape = RoundedCornerShape(8.dp),
|
||||||
|
elevation = CardDefaults.cardElevation(defaultElevation = 1.dp)
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(12.dp),
|
||||||
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
modifier = Modifier.weight(1f)
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.Folder,
|
||||||
|
contentDescription = null,
|
||||||
|
tint = MaterialTheme.colorScheme.primary,
|
||||||
|
modifier = Modifier.size(18.dp)
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.width(12.dp))
|
||||||
|
Text(
|
||||||
|
text = path,
|
||||||
|
style = MaterialTheme.typography.bodyLarge,
|
||||||
|
fontWeight = FontWeight.Medium
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Row(
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(4.dp)
|
||||||
|
) {
|
||||||
|
IconButton(
|
||||||
|
onClick = onUpdate,
|
||||||
|
enabled = !isLoading,
|
||||||
|
modifier = Modifier.size(32.dp)
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.Update,
|
||||||
|
contentDescription = null,
|
||||||
|
tint = MaterialTheme.colorScheme.secondary,
|
||||||
|
modifier = Modifier.size(16.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
IconButton(
|
||||||
|
onClick = onUpdateFullClone,
|
||||||
|
enabled = !isLoading,
|
||||||
|
modifier = Modifier.size(32.dp)
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.PlayArrow,
|
||||||
|
contentDescription = null,
|
||||||
|
tint = MaterialTheme.colorScheme.tertiary,
|
||||||
|
modifier = Modifier.size(16.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
IconButton(
|
||||||
|
onClick = onDelete,
|
||||||
|
enabled = !isLoading,
|
||||||
|
modifier = Modifier.size(32.dp)
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.Delete,
|
||||||
|
contentDescription = null,
|
||||||
|
tint = MaterialTheme.colorScheme.error,
|
||||||
|
modifier = Modifier.size(16.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 启用功能状态卡片组件
|
* 启用功能状态卡片组件
|
||||||
*/
|
*/
|
||||||
@@ -537,3 +699,157 @@ fun TryUmountContent(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Kstat配置内容组件
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
fun KstatConfigContent(
|
||||||
|
kstatConfigs: Set<String>,
|
||||||
|
addKstatPaths: Set<String>,
|
||||||
|
isLoading: Boolean,
|
||||||
|
onAddKstatStatically: () -> Unit,
|
||||||
|
onAddKstat: () -> Unit,
|
||||||
|
onRemoveKstatConfig: (String) -> Unit,
|
||||||
|
onRemoveAddKstat: (String) -> Unit,
|
||||||
|
onUpdateKstat: (String) -> Unit,
|
||||||
|
onUpdateKstatFullClone: (String) -> Unit
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(12.dp)
|
||||||
|
) {
|
||||||
|
// 标题和添加按钮
|
||||||
|
UnifiedButtonRow(
|
||||||
|
primaryButton = {
|
||||||
|
Row(
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
|
) {
|
||||||
|
FloatingActionButton(
|
||||||
|
onClick = onAddKstatStatically,
|
||||||
|
modifier = Modifier.size(48.dp),
|
||||||
|
containerColor = MaterialTheme.colorScheme.primary,
|
||||||
|
contentColor = MaterialTheme.colorScheme.onPrimary
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.Settings,
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = Modifier.size(20.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
FloatingActionButton(
|
||||||
|
onClick = onAddKstat,
|
||||||
|
modifier = Modifier.size(48.dp),
|
||||||
|
containerColor = MaterialTheme.colorScheme.secondary,
|
||||||
|
contentColor = MaterialTheme.colorScheme.onSecondary
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.Add,
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = Modifier.size(20.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
secondaryButtons = {
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.kstat_config_management),
|
||||||
|
style = MaterialTheme.typography.titleLarge,
|
||||||
|
fontWeight = FontWeight.Bold
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// 说明卡片
|
||||||
|
Card(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
colors = CardDefaults.cardColors(
|
||||||
|
containerColor = MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.4f)
|
||||||
|
),
|
||||||
|
shape = RoundedCornerShape(12.dp)
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.padding(12.dp),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.kstat_config_description_title),
|
||||||
|
style = MaterialTheme.typography.titleMedium,
|
||||||
|
fontWeight = FontWeight.Medium,
|
||||||
|
color = MaterialTheme.colorScheme.primary
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.kstat_config_description_add_statically),
|
||||||
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.kstat_config_description_add),
|
||||||
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.kstat_config_description_update),
|
||||||
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.kstat_config_description_update_full_clone),
|
||||||
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 静态Kstat配置列表
|
||||||
|
if (kstatConfigs.isNotEmpty()) {
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.static_kstat_config),
|
||||||
|
style = MaterialTheme.typography.titleMedium,
|
||||||
|
fontWeight = FontWeight.Bold
|
||||||
|
)
|
||||||
|
LazyColumn(
|
||||||
|
modifier = Modifier.weight(0.5f),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(6.dp)
|
||||||
|
) {
|
||||||
|
items(kstatConfigs.toList()) { config ->
|
||||||
|
KstatConfigItemCard(
|
||||||
|
config = config,
|
||||||
|
onDelete = { onRemoveKstatConfig(config) },
|
||||||
|
isLoading = isLoading
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add Kstat路径列表
|
||||||
|
if (addKstatPaths.isNotEmpty()) {
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.kstat_path_management),
|
||||||
|
style = MaterialTheme.typography.titleMedium,
|
||||||
|
fontWeight = FontWeight.Bold
|
||||||
|
)
|
||||||
|
LazyColumn(
|
||||||
|
modifier = Modifier.weight(0.5f),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(6.dp)
|
||||||
|
) {
|
||||||
|
items(addKstatPaths.toList()) { path ->
|
||||||
|
AddKstatPathItemCard(
|
||||||
|
path = path,
|
||||||
|
onDelete = { onRemoveAddKstat(path) },
|
||||||
|
onUpdate = { onUpdateKstat(path) },
|
||||||
|
onUpdateFullClone = { onUpdateKstatFullClone(path) },
|
||||||
|
isLoading = isLoading
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 空状态显示
|
||||||
|
if (kstatConfigs.isEmpty() && addKstatPaths.isEmpty()) {
|
||||||
|
EmptyStateCard(
|
||||||
|
message = stringResource(R.string.no_kstat_config_message)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,7 +8,9 @@ import com.dergoogler.mmrl.platform.Platform.Companion.context
|
|||||||
import com.sukisu.ultra.Natives
|
import com.sukisu.ultra.Natives
|
||||||
import com.sukisu.ultra.R
|
import com.sukisu.ultra.R
|
||||||
import com.topjohnwu.superuser.Shell
|
import com.topjohnwu.superuser.Shell
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import java.io.FileOutputStream
|
import java.io.FileOutputStream
|
||||||
@@ -34,6 +36,8 @@ object SuSFSManager {
|
|||||||
private const val KEY_SDCARD_PATH = "sdcard_path"
|
private const val KEY_SDCARD_PATH = "sdcard_path"
|
||||||
private const val KEY_ENABLE_LOG = "enable_log"
|
private const val KEY_ENABLE_LOG = "enable_log"
|
||||||
private const val KEY_EXECUTE_IN_POST_FS_DATA = "execute_in_post_fs_data"
|
private const val KEY_EXECUTE_IN_POST_FS_DATA = "execute_in_post_fs_data"
|
||||||
|
private const val KEY_KSTAT_CONFIGS = "kstat_configs"
|
||||||
|
private const val KEY_ADD_KSTAT_PATHS = "add_kstat_paths"
|
||||||
private const val SUSFS_BINARY_BASE_NAME = "ksu_susfs"
|
private const val SUSFS_BINARY_BASE_NAME = "ksu_susfs"
|
||||||
private const val DEFAULT_UNAME = "default"
|
private const val DEFAULT_UNAME = "default"
|
||||||
private const val DEFAULT_BUILD_TIME = "default"
|
private const val DEFAULT_BUILD_TIME = "default"
|
||||||
@@ -198,13 +202,56 @@ object SuSFSManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 保存执行位置设置
|
* 保存执行位置设置并实时更新状态
|
||||||
*/
|
*/
|
||||||
fun saveExecuteInPostFsData(context: Context, executeInPostFsData: Boolean) {
|
fun saveExecuteInPostFsData(context: Context, executeInPostFsData: Boolean) {
|
||||||
|
val oldExecuteInPostFsData = getExecuteInPostFsData(context)
|
||||||
|
|
||||||
getPrefs(context).edit().apply {
|
getPrefs(context).edit().apply {
|
||||||
putBoolean(KEY_EXECUTE_IN_POST_FS_DATA, executeInPostFsData)
|
putBoolean(KEY_EXECUTE_IN_POST_FS_DATA, executeInPostFsData)
|
||||||
apply()
|
apply()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (oldExecuteInPostFsData != executeInPostFsData && isAutoStartEnabled(context)) {
|
||||||
|
CoroutineScope(Dispatchers.Default).launch {
|
||||||
|
try {
|
||||||
|
// 删除旧的模块状态
|
||||||
|
removeMagiskModule()
|
||||||
|
|
||||||
|
// 重新创建模块以应用新的执行位置设置
|
||||||
|
val success = createMagiskModule(context)
|
||||||
|
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
if (success) {
|
||||||
|
Toast.makeText(
|
||||||
|
context,
|
||||||
|
if (executeInPostFsData) {
|
||||||
|
context.getString(R.string.susfs_execute_location_updated_post_fs_data)
|
||||||
|
} else {
|
||||||
|
context.getString(R.string.susfs_execute_location_updated_service)
|
||||||
|
},
|
||||||
|
Toast.LENGTH_SHORT
|
||||||
|
).show()
|
||||||
|
} else {
|
||||||
|
Toast.makeText(
|
||||||
|
context,
|
||||||
|
context.getString(R.string.susfs_execute_location_update_failed),
|
||||||
|
Toast.LENGTH_SHORT
|
||||||
|
).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
Toast.makeText(
|
||||||
|
context,
|
||||||
|
context.getString(R.string.susfs_execute_location_update_error, e.message ?: "Unknown error"),
|
||||||
|
Toast.LENGTH_SHORT
|
||||||
|
).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -343,6 +390,40 @@ object SuSFSManager {
|
|||||||
return getPrefs(context).getStringSet(KEY_TRY_UMOUNTS, emptySet()) ?: emptySet()
|
return getPrefs(context).getStringSet(KEY_TRY_UMOUNTS, emptySet()) ?: emptySet()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存Kstat配置列表
|
||||||
|
*/
|
||||||
|
fun saveKstatConfigs(context: Context, configs: Set<String>) {
|
||||||
|
getPrefs(context).edit().apply {
|
||||||
|
putStringSet(KEY_KSTAT_CONFIGS, configs)
|
||||||
|
apply()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取Kstat配置列表
|
||||||
|
*/
|
||||||
|
fun getKstatConfigs(context: Context): Set<String> {
|
||||||
|
return getPrefs(context).getStringSet(KEY_KSTAT_CONFIGS, emptySet()) ?: emptySet()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存Add Kstat路径列表
|
||||||
|
*/
|
||||||
|
fun saveAddKstatPaths(context: Context, paths: Set<String>) {
|
||||||
|
getPrefs(context).edit().apply {
|
||||||
|
putStringSet(KEY_ADD_KSTAT_PATHS, paths)
|
||||||
|
apply()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取Add Kstat路径列表
|
||||||
|
*/
|
||||||
|
fun getAddKstatPaths(context: Context): Set<String> {
|
||||||
|
return getPrefs(context).getStringSet(KEY_ADD_KSTAT_PATHS, emptySet()) ?: emptySet()
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 保存Android Data路径
|
* 保存Android Data路径
|
||||||
*/
|
*/
|
||||||
@@ -462,11 +543,14 @@ object SuSFSManager {
|
|||||||
val androidDataPath = getAndroidDataPath(context)
|
val androidDataPath = getAndroidDataPath(context)
|
||||||
val sdcardPath = getSdcardPath(context)
|
val sdcardPath = getSdcardPath(context)
|
||||||
val enableLog = getEnableLogState(context)
|
val enableLog = getEnableLogState(context)
|
||||||
|
val kstatConfigs = getKstatConfigs(context)
|
||||||
|
val addKstatPaths = getAddKstatPaths(context)
|
||||||
|
|
||||||
// 生成并创建service.sh
|
// 生成并创建service.sh
|
||||||
val serviceScript = ScriptGenerator.generateServiceScript(
|
val serviceScript = ScriptGenerator.generateServiceScript(
|
||||||
targetPath, unameValue, buildTimeValue, susPaths,
|
targetPath, unameValue, buildTimeValue, susPaths,
|
||||||
androidDataPath, sdcardPath, enableLog, executeInPostFsData
|
androidDataPath, sdcardPath, enableLog, executeInPostFsData,
|
||||||
|
kstatConfigs, addKstatPaths
|
||||||
)
|
)
|
||||||
val createServiceResult = shell.newJob()
|
val createServiceResult = shell.newJob()
|
||||||
.add("cat > $MODULE_PATH/service.sh << 'EOF'\n$serviceScript\nEOF")
|
.add("cat > $MODULE_PATH/service.sh << 'EOF'\n$serviceScript\nEOF")
|
||||||
@@ -794,6 +878,133 @@ object SuSFSManager {
|
|||||||
return executeSusfsCommand(context, "run_try_umount")
|
return executeSusfsCommand(context, "run_try_umount")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加Kstat静态配置
|
||||||
|
*/
|
||||||
|
suspend fun addKstatStatically(
|
||||||
|
context: Context,
|
||||||
|
path: String,
|
||||||
|
ino: String,
|
||||||
|
dev: String,
|
||||||
|
nlink: String,
|
||||||
|
size: String,
|
||||||
|
atime: String,
|
||||||
|
atimeNsec: String,
|
||||||
|
mtime: String,
|
||||||
|
mtimeNsec: String,
|
||||||
|
ctime: String,
|
||||||
|
ctimeNsec: String,
|
||||||
|
blocks: String,
|
||||||
|
blksize: String
|
||||||
|
): Boolean {
|
||||||
|
val command = "add_sus_kstat_statically '$path' '$ino' '$dev' '$nlink' '$size' '$atime' '$atimeNsec' '$mtime' '$mtimeNsec' '$ctime' '$ctimeNsec' '$blocks' '$blksize'"
|
||||||
|
val success = executeSusfsCommand(context, command)
|
||||||
|
if (success) {
|
||||||
|
val currentConfigs = getKstatConfigs(context).toMutableSet()
|
||||||
|
val configEntry = "$path|$ino|$dev|$nlink|$size|$atime|$atimeNsec|$mtime|$mtimeNsec|$ctime|$ctimeNsec|$blocks|$blksize"
|
||||||
|
currentConfigs.add(configEntry)
|
||||||
|
saveKstatConfigs(context, currentConfigs)
|
||||||
|
|
||||||
|
// 如果开启了开机自启动,更新模块
|
||||||
|
if (isAutoStartEnabled(context)) {
|
||||||
|
createMagiskModule(context)
|
||||||
|
}
|
||||||
|
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
Toast.makeText(context, context.getString(R.string.kstat_static_config_added, path), Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return success
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移除Kstat配置
|
||||||
|
*/
|
||||||
|
suspend fun removeKstatConfig(context: Context, config: String): Boolean {
|
||||||
|
val currentConfigs = getKstatConfigs(context).toMutableSet()
|
||||||
|
currentConfigs.remove(config)
|
||||||
|
saveKstatConfigs(context, currentConfigs)
|
||||||
|
|
||||||
|
// 如果开启了开机自启动,更新模块
|
||||||
|
if (isAutoStartEnabled(context)) {
|
||||||
|
createMagiskModule(context)
|
||||||
|
}
|
||||||
|
|
||||||
|
val parts = config.split("|")
|
||||||
|
val path = if (parts.isNotEmpty()) parts[0] else config
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
Toast.makeText(context, context.getString(R.string.kstat_config_removed, path), Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加Kstat路径
|
||||||
|
*/
|
||||||
|
suspend fun addKstat(context: Context, path: String): Boolean {
|
||||||
|
val success = executeSusfsCommand(context, "add_sus_kstat '$path'")
|
||||||
|
if (success) {
|
||||||
|
val currentPaths = getAddKstatPaths(context).toMutableSet()
|
||||||
|
currentPaths.add(path)
|
||||||
|
saveAddKstatPaths(context, currentPaths)
|
||||||
|
|
||||||
|
// 如果开启了开机自启动,更新模块
|
||||||
|
if (isAutoStartEnabled(context)) {
|
||||||
|
createMagiskModule(context)
|
||||||
|
}
|
||||||
|
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
Toast.makeText(context, context.getString(R.string.kstat_path_added, path), Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return success
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移除Add Kstat路径
|
||||||
|
*/
|
||||||
|
suspend fun removeAddKstat(context: Context, path: String): Boolean {
|
||||||
|
val currentPaths = getAddKstatPaths(context).toMutableSet()
|
||||||
|
currentPaths.remove(path)
|
||||||
|
saveAddKstatPaths(context, currentPaths)
|
||||||
|
|
||||||
|
// 如果开启了开机自启动,更新模块
|
||||||
|
if (isAutoStartEnabled(context)) {
|
||||||
|
createMagiskModule(context)
|
||||||
|
}
|
||||||
|
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
Toast.makeText(context, context.getString(R.string.kstat_path_removed, path), Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新Kstat
|
||||||
|
*/
|
||||||
|
suspend fun updateKstat(context: Context, path: String): Boolean {
|
||||||
|
val success = executeSusfsCommand(context, "update_sus_kstat '$path'")
|
||||||
|
if (success) {
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
Toast.makeText(context, context.getString(R.string.kstat_updated, path), Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return success
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 完整克隆更新Kstat
|
||||||
|
*/
|
||||||
|
suspend fun updateKstatFullClone(context: Context, path: String): Boolean {
|
||||||
|
val success = executeSusfsCommand(context, "update_sus_kstat_full_clone '$path'")
|
||||||
|
if (success) {
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
Toast.makeText(context, context.getString(R.string.kstat_full_clone_updated, path), Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return success
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设置Android Data路径
|
* 设置Android Data路径
|
||||||
*/
|
*/
|
||||||
@@ -905,6 +1116,8 @@ object SuSFSManager {
|
|||||||
val susPaths = getSusPaths(context)
|
val susPaths = getSusPaths(context)
|
||||||
val susMounts = getSusMounts(context)
|
val susMounts = getSusMounts(context)
|
||||||
val tryUmounts = getTryUmounts(context)
|
val tryUmounts = getTryUmounts(context)
|
||||||
|
val kstatConfigs = getKstatConfigs(context)
|
||||||
|
val addKstatPaths = getAddKstatPaths(context)
|
||||||
val enabledFeatures = runBlocking {
|
val enabledFeatures = runBlocking {
|
||||||
getEnabledFeatures(context)
|
getEnabledFeatures(context)
|
||||||
}
|
}
|
||||||
@@ -914,6 +1127,8 @@ object SuSFSManager {
|
|||||||
susPaths.isNotEmpty() ||
|
susPaths.isNotEmpty() ||
|
||||||
susMounts.isNotEmpty() ||
|
susMounts.isNotEmpty() ||
|
||||||
tryUmounts.isNotEmpty() ||
|
tryUmounts.isNotEmpty() ||
|
||||||
|
kstatConfigs.isNotEmpty() ||
|
||||||
|
addKstatPaths.isNotEmpty() ||
|
||||||
enabledFeatures.any { it.isEnabled }
|
enabledFeatures.any { it.isEnabled }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,9 @@ object ScriptGenerator {
|
|||||||
androidDataPath: String,
|
androidDataPath: String,
|
||||||
sdcardPath: String,
|
sdcardPath: String,
|
||||||
enableLog: Boolean,
|
enableLog: Boolean,
|
||||||
executeInPostFsData: Boolean = false
|
executeInPostFsData: Boolean = false,
|
||||||
|
kstatConfigs: Set<String> = emptySet(),
|
||||||
|
addKstatPaths: Set<String> = emptySet()
|
||||||
): String {
|
): String {
|
||||||
return buildString {
|
return buildString {
|
||||||
appendLine("#!/system/bin/sh")
|
appendLine("#!/system/bin/sh")
|
||||||
@@ -42,7 +44,7 @@ object ScriptGenerator {
|
|||||||
appendLine("# 检查SuSFS二进制文件")
|
appendLine("# 检查SuSFS二进制文件")
|
||||||
appendLine("SUSFS_BIN=\"$targetPath\"")
|
appendLine("SUSFS_BIN=\"$targetPath\"")
|
||||||
appendLine("if [ ! -f \"\$SUSFS_BIN\" ]; then")
|
appendLine("if [ ! -f \"\$SUSFS_BIN\" ]; then")
|
||||||
appendLine(" echo \"\$(get_current_time): SuSFS二进制文件未找到: \$SUSFS_BIN\" >> \"\$LOG_FILE\"")
|
appendLine(" echo \"$(get_current_time): SuSFS二进制文件未找到: \$SUSFS_BIN\" >> \"\$LOG_FILE\"")
|
||||||
appendLine(" exit 1")
|
appendLine(" exit 1")
|
||||||
appendLine("fi")
|
appendLine("fi")
|
||||||
appendLine()
|
appendLine()
|
||||||
@@ -51,14 +53,14 @@ object ScriptGenerator {
|
|||||||
appendLine("# 设置日志启用状态")
|
appendLine("# 设置日志启用状态")
|
||||||
val logValue = if (enableLog) 1 else 0
|
val logValue = if (enableLog) 1 else 0
|
||||||
appendLine("\"\$SUSFS_BIN\" enable_log $logValue")
|
appendLine("\"\$SUSFS_BIN\" enable_log $logValue")
|
||||||
appendLine("echo \"\$(get_current_time): 日志功能设置为: ${if (enableLog) "启用" else "禁用"}\" >> \"\$LOG_FILE\"")
|
appendLine("echo \"$(get_current_time): 日志功能设置为: ${if (enableLog) "启用" else "禁用"}\" >> \"\$LOG_FILE\"")
|
||||||
appendLine()
|
appendLine()
|
||||||
|
|
||||||
// 设置Android Data路径
|
// 设置Android Data路径
|
||||||
if (androidDataPath != "/sdcard/Android/data") {
|
if (androidDataPath != "/sdcard/Android/data") {
|
||||||
appendLine("# 设置Android Data路径")
|
appendLine("# 设置Android Data路径")
|
||||||
appendLine("\"\$SUSFS_BIN\" set_android_data_root_path '$androidDataPath'")
|
appendLine("\"\$SUSFS_BIN\" set_android_data_root_path '$androidDataPath'")
|
||||||
appendLine("echo \"\$(get_current_time): Android Data路径设置为: $androidDataPath\" >> \"\$LOG_FILE\"")
|
appendLine("echo \"$(get_current_time): Android Data路径设置为: $androidDataPath\" >> \"\$LOG_FILE\"")
|
||||||
appendLine()
|
appendLine()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,7 +68,7 @@ object ScriptGenerator {
|
|||||||
if (sdcardPath != "/sdcard") {
|
if (sdcardPath != "/sdcard") {
|
||||||
appendLine("# 设置SD卡路径")
|
appendLine("# 设置SD卡路径")
|
||||||
appendLine("\"\$SUSFS_BIN\" set_sdcard_root_path '$sdcardPath'")
|
appendLine("\"\$SUSFS_BIN\" set_sdcard_root_path '$sdcardPath'")
|
||||||
appendLine("echo \"\$(get_current_time): SD卡路径设置为: $sdcardPath\" >> \"\$LOG_FILE\"")
|
appendLine("echo \"$(get_current_time): SD卡路径设置为: $sdcardPath\" >> \"\$LOG_FILE\"")
|
||||||
appendLine()
|
appendLine()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,7 +77,32 @@ object ScriptGenerator {
|
|||||||
appendLine("# 添加SUS路径")
|
appendLine("# 添加SUS路径")
|
||||||
susPaths.forEach { path ->
|
susPaths.forEach { path ->
|
||||||
appendLine("\"\$SUSFS_BIN\" add_sus_path '$path'")
|
appendLine("\"\$SUSFS_BIN\" add_sus_path '$path'")
|
||||||
appendLine("echo \"\$(get_current_time): 添加SUS路径: $path\" >> \"\$LOG_FILE\"")
|
appendLine("echo \"$(get_current_time): 添加SUS路径: $path\" >> \"\$LOG_FILE\"")
|
||||||
|
}
|
||||||
|
appendLine()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加Kstat静态配置
|
||||||
|
if (kstatConfigs.isNotEmpty()) {
|
||||||
|
appendLine("# 添加Kstat静态配置")
|
||||||
|
kstatConfigs.forEach { config ->
|
||||||
|
val parts = config.split("|")
|
||||||
|
if (parts.size >= 13) {
|
||||||
|
val path = parts[0]
|
||||||
|
val params = parts.drop(1).joinToString("' '", "'", "'")
|
||||||
|
appendLine("\"\$SUSFS_BIN\" add_sus_kstat_statically $params")
|
||||||
|
appendLine("echo \"$(get_current_time): 添加Kstat静态配置: $path\" >> \"\$LOG_FILE\"")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
appendLine()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加Kstat路径
|
||||||
|
if (addKstatPaths.isNotEmpty()) {
|
||||||
|
appendLine("# 添加Kstat路径")
|
||||||
|
addKstatPaths.forEach { path ->
|
||||||
|
appendLine("\"\$SUSFS_BIN\" add_sus_kstat '$path'")
|
||||||
|
appendLine("echo \"$(get_current_time): 添加Kstat路径: $path\" >> \"\$LOG_FILE\"")
|
||||||
}
|
}
|
||||||
appendLine()
|
appendLine()
|
||||||
}
|
}
|
||||||
@@ -84,38 +111,38 @@ object ScriptGenerator {
|
|||||||
if (!executeInPostFsData && (unameValue != "default" || buildTimeValue != "default")) {
|
if (!executeInPostFsData && (unameValue != "default" || buildTimeValue != "default")) {
|
||||||
appendLine("# 设置uname和构建时间")
|
appendLine("# 设置uname和构建时间")
|
||||||
appendLine("\"\$SUSFS_BIN\" set_uname '$unameValue' '$buildTimeValue'")
|
appendLine("\"\$SUSFS_BIN\" set_uname '$unameValue' '$buildTimeValue'")
|
||||||
appendLine("echo \"\$(get_current_time): 设置uname为: $unameValue, 构建时间为: $buildTimeValue\" >> \"\$LOG_FILE\"")
|
appendLine("echo \"$(get_current_time): 设置uname为: $unameValue, 构建时间为: $buildTimeValue\" >> \"\$LOG_FILE\"")
|
||||||
appendLine()
|
appendLine()
|
||||||
}
|
}
|
||||||
|
|
||||||
appendLine("# 隐弱BL 来自 Shamiko 脚本")
|
appendLine("# 隐弱BL 来自 Shamiko 脚本")
|
||||||
appendLine("check_reset_prop() {")
|
appendLine("check_reset_prop() {")
|
||||||
appendLine("local NAME=\$1")
|
appendLine("local NAME=$1")
|
||||||
appendLine("local EXPECTED=\$2")
|
appendLine("local EXPECTED=$2")
|
||||||
appendLine("local VALUE=\$(resetprop \$NAME)")
|
appendLine("local VALUE=$(resetprop \$NAME)")
|
||||||
appendLine("[ -z \$VALUE ] || [ \$VALUE = \$EXPECTED ] || resetprop \$NAME \$EXPECTED")
|
appendLine("[ -z \$VALUE ] || [ \$VALUE = \$EXPECTED ] || resetprop \$NAME \$EXPECTED")
|
||||||
appendLine("}")
|
appendLine("}")
|
||||||
appendLine()
|
appendLine()
|
||||||
appendLine("check_missing_prop() {")
|
appendLine("check_missing_prop() {")
|
||||||
appendLine(" local NAME=\$1")
|
appendLine(" local NAME=$1")
|
||||||
appendLine(" local EXPECTED=\$2")
|
appendLine(" local EXPECTED=$2")
|
||||||
appendLine(" local VALUE=\$(resetprop \$NAME)")
|
appendLine(" local VALUE=$(resetprop \$NAME)")
|
||||||
appendLine(" [ -z \$VALUE ] && resetprop \$NAME \$EXPECTED")
|
appendLine(" [ -z \$VALUE ] && resetprop \$NAME \$EXPECTED")
|
||||||
appendLine("}")
|
appendLine("}")
|
||||||
appendLine()
|
appendLine()
|
||||||
appendLine("check_missing_match_prop() {")
|
appendLine("check_missing_match_prop() {")
|
||||||
appendLine(" local NAME=\$1")
|
appendLine(" local NAME=$1")
|
||||||
appendLine(" local EXPECTED=\$2")
|
appendLine(" local EXPECTED=$2")
|
||||||
appendLine(" local VALUE=\$(resetprop \$NAME)")
|
appendLine(" local VALUE=$(resetprop \$NAME)")
|
||||||
appendLine(" [ -z \$VALUE ] || [ \$VALUE = \$EXPECTED ] || resetprop \$NAME \$EXPECTED")
|
appendLine(" [ -z \$VALUE ] || [ \$VALUE = \$EXPECTED ] || resetprop \$NAME \$EXPECTED")
|
||||||
appendLine(" [ -z \$VALUE ] && resetprop \$NAME \$EXPECTED")
|
appendLine(" [ -z \$VALUE ] && resetprop \$NAME \$EXPECTED")
|
||||||
appendLine("}")
|
appendLine("}")
|
||||||
appendLine()
|
appendLine()
|
||||||
appendLine("contains_reset_prop() {")
|
appendLine("contains_reset_prop() {")
|
||||||
appendLine("local NAME=\$1")
|
appendLine("local NAME=$1")
|
||||||
appendLine("local CONTAINS=\$2")
|
appendLine("local CONTAINS=$2")
|
||||||
appendLine("local NEWVAL=\$3")
|
appendLine("local NEWVAL=$3")
|
||||||
appendLine("[[ \"\$(resetprop \$NAME)\" = *\"\$CONTAINS\"* ]] && resetprop \$NAME \$NEWVAL")
|
appendLine("[[ \"$(resetprop \$NAME)\" = *\"\$CONTAINS\"* ]] && resetprop \$NAME \$NEWVAL")
|
||||||
appendLine("}")
|
appendLine("}")
|
||||||
appendLine()
|
appendLine()
|
||||||
appendLine("resetprop -w sys.boot_completed 0")
|
appendLine("resetprop -w sys.boot_completed 0")
|
||||||
@@ -163,13 +190,13 @@ object ScriptGenerator {
|
|||||||
appendLine("contains_reset_prop \"vendor.boot.bootmode\" \"recovery\" \"unknown\"")
|
appendLine("contains_reset_prop \"vendor.boot.bootmode\" \"recovery\" \"unknown\"")
|
||||||
appendLine()
|
appendLine()
|
||||||
appendLine("# Hide cloudphone detection")
|
appendLine("# Hide cloudphone detection")
|
||||||
appendLine("[ -n \"\$(resetprop ro.kernel.qemu)\" ] && resetprop ro.kernel.qemu \"\"")
|
appendLine("[ -n \"$(resetprop ro.kernel.qemu)\" ] && resetprop ro.kernel.qemu \"\"")
|
||||||
appendLine()
|
appendLine()
|
||||||
appendLine("# fake encryption status")
|
appendLine("# fake encryption status")
|
||||||
appendLine("check_reset_prop \"ro.crypto.state\" \"encrypted\"")
|
appendLine("check_reset_prop \"ro.crypto.state\" \"encrypted\"")
|
||||||
appendLine()
|
appendLine()
|
||||||
|
|
||||||
appendLine("echo \"\$(get_current_time): Service脚本执行完成\" >> \"\$LOG_FILE\"")
|
appendLine("echo \"$(get_current_time): Service脚本执行完成\" >> \"\$LOG_FILE\"")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -202,22 +229,22 @@ object ScriptGenerator {
|
|||||||
appendLine("# 检查SuSFS二进制文件")
|
appendLine("# 检查SuSFS二进制文件")
|
||||||
appendLine("SUSFS_BIN=\"$targetPath\"")
|
appendLine("SUSFS_BIN=\"$targetPath\"")
|
||||||
appendLine("if [ ! -f \"\$SUSFS_BIN\" ]; then")
|
appendLine("if [ ! -f \"\$SUSFS_BIN\" ]; then")
|
||||||
appendLine(" echo \"\$(get_current_time): SuSFS二进制文件未找到: \$SUSFS_BIN\" >> \"\$LOG_FILE\"")
|
appendLine(" echo \"$(get_current_time): SuSFS二进制文件未找到: \$SUSFS_BIN\" >> \"\$LOG_FILE\"")
|
||||||
appendLine(" exit 1")
|
appendLine(" exit 1")
|
||||||
appendLine("fi")
|
appendLine("fi")
|
||||||
appendLine()
|
appendLine()
|
||||||
appendLine("echo \"\$(get_current_time): Post-FS-Data脚本开始执行\" >> \"\$LOG_FILE\"")
|
appendLine("echo \"$(get_current_time): Post-FS-Data脚本开始执行\" >> \"\$LOG_FILE\"")
|
||||||
appendLine()
|
appendLine()
|
||||||
|
|
||||||
// 设置uname和构建时间 - 只有在选择在post-fs-data中执行时才执行
|
// 设置uname和构建时间 - 只有在选择在post-fs-data中执行时才执行
|
||||||
if (executeInPostFsData && (unameValue != "default" || buildTimeValue != "default")) {
|
if (executeInPostFsData && (unameValue != "default" || buildTimeValue != "default")) {
|
||||||
appendLine("# 设置uname和构建时间")
|
appendLine("# 设置uname和构建时间")
|
||||||
appendLine("\"\$SUSFS_BIN\" set_uname '$unameValue' '$buildTimeValue'")
|
appendLine("\"\$SUSFS_BIN\" set_uname '$unameValue' '$buildTimeValue'")
|
||||||
appendLine("echo \"\$(get_current_time): 设置uname为: $unameValue, 构建时间为: $buildTimeValue\" >> \"\$LOG_FILE\"")
|
appendLine("echo \"$(get_current_time): 设置uname为: $unameValue, 构建时间为: $buildTimeValue\" >> \"\$LOG_FILE\"")
|
||||||
appendLine()
|
appendLine()
|
||||||
}
|
}
|
||||||
|
|
||||||
appendLine("echo \"\$(get_current_time): Post-FS-Data脚本执行完成\" >> \"\$LOG_FILE\"")
|
appendLine("echo \"$(get_current_time): Post-FS-Data脚本执行完成\" >> \"\$LOG_FILE\"")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -246,12 +273,12 @@ object ScriptGenerator {
|
|||||||
appendLine(" date '+%Y-%m-%d %H:%M:%S'")
|
appendLine(" date '+%Y-%m-%d %H:%M:%S'")
|
||||||
appendLine("}")
|
appendLine("}")
|
||||||
appendLine()
|
appendLine()
|
||||||
appendLine("echo \"\$(get_current_time): Post-Mount脚本开始执行\" >> \"\$LOG_FILE\"")
|
appendLine("echo \"$(get_current_time): Post-Mount脚本开始执行\" >> \"\$LOG_FILE\"")
|
||||||
appendLine()
|
appendLine()
|
||||||
appendLine("# 检查SuSFS二进制文件")
|
appendLine("# 检查SuSFS二进制文件")
|
||||||
appendLine("SUSFS_BIN=\"$targetPath\"")
|
appendLine("SUSFS_BIN=\"$targetPath\"")
|
||||||
appendLine("if [ ! -f \"\$SUSFS_BIN\" ]; then")
|
appendLine("if [ ! -f \"\$SUSFS_BIN\" ]; then")
|
||||||
appendLine(" echo \"\$(get_current_time): SuSFS二进制文件未找到: \$SUSFS_BIN\" >> \"\$LOG_FILE\"")
|
appendLine(" echo \"$(get_current_time): SuSFS二进制文件未找到: \$SUSFS_BIN\" >> \"\$LOG_FILE\"")
|
||||||
appendLine(" exit 1")
|
appendLine(" exit 1")
|
||||||
appendLine("fi")
|
appendLine("fi")
|
||||||
appendLine()
|
appendLine()
|
||||||
@@ -261,7 +288,7 @@ object ScriptGenerator {
|
|||||||
appendLine("# 添加SUS挂载")
|
appendLine("# 添加SUS挂载")
|
||||||
susMounts.forEach { mount ->
|
susMounts.forEach { mount ->
|
||||||
appendLine("\"\$SUSFS_BIN\" add_sus_mount '$mount'")
|
appendLine("\"\$SUSFS_BIN\" add_sus_mount '$mount'")
|
||||||
appendLine("echo \"\$(get_current_time): 添加SUS挂载: $mount\" >> \"\$LOG_FILE\"")
|
appendLine("echo \"$(get_current_time): 添加SUS挂载: $mount\" >> \"\$LOG_FILE\"")
|
||||||
}
|
}
|
||||||
appendLine()
|
appendLine()
|
||||||
}
|
}
|
||||||
@@ -275,13 +302,13 @@ object ScriptGenerator {
|
|||||||
val path = parts[0]
|
val path = parts[0]
|
||||||
val mode = parts[1]
|
val mode = parts[1]
|
||||||
appendLine("\"\$SUSFS_BIN\" add_try_umount '$path' $mode")
|
appendLine("\"\$SUSFS_BIN\" add_try_umount '$path' $mode")
|
||||||
appendLine("echo \"\$(get_current_time): 添加尝试卸载: $path (模式: $mode)\" >> \"\$LOG_FILE\"")
|
appendLine("echo \"$(get_current_time): 添加尝试卸载: $path (模式: $mode)\" >> \"\$LOG_FILE\"")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
appendLine()
|
appendLine()
|
||||||
}
|
}
|
||||||
|
|
||||||
appendLine("echo \"\$(get_current_time): Post-Mount脚本执行完成\" >> \"\$LOG_FILE\"")
|
appendLine("echo \"$(get_current_time): Post-Mount脚本执行完成\" >> \"\$LOG_FILE\"")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -306,16 +333,16 @@ object ScriptGenerator {
|
|||||||
appendLine(" date '+%Y-%m-%d %H:%M:%S'")
|
appendLine(" date '+%Y-%m-%d %H:%M:%S'")
|
||||||
appendLine("}")
|
appendLine("}")
|
||||||
appendLine()
|
appendLine()
|
||||||
appendLine("echo \"\$(get_current_time): Boot-Completed脚本开始执行\" >> \"\$LOG_FILE\"")
|
appendLine("echo \"$(get_current_time): Boot-Completed脚本开始执行\" >> \"\$LOG_FILE\"")
|
||||||
appendLine()
|
appendLine()
|
||||||
appendLine("# 检查SuSFS二进制文件")
|
appendLine("# 检查SuSFS二进制文件")
|
||||||
appendLine("SUSFS_BIN=\"$targetPath\"")
|
appendLine("SUSFS_BIN=\"$targetPath\"")
|
||||||
appendLine("if [ ! -f \"\$SUSFS_BIN\" ]; then")
|
appendLine("if [ ! -f \"\$SUSFS_BIN\" ]; then")
|
||||||
appendLine(" echo \"\$(get_current_time): SuSFS二进制文件未找到: \$SUSFS_BIN\" >> \"\$LOG_FILE\"")
|
appendLine(" echo \"$(get_current_time): SuSFS二进制文件未找到: \$SUSFS_BIN\" >> \"\$LOG_FILE\"")
|
||||||
appendLine(" exit 1")
|
appendLine(" exit 1")
|
||||||
appendLine("fi")
|
appendLine("fi")
|
||||||
appendLine()
|
appendLine()
|
||||||
appendLine("echo \"\$(get_current_time): Boot-Completed脚本执行完成\" >> \"\$LOG_FILE\"")
|
appendLine("echo \"$(get_current_time): Boot-Completed脚本执行完成\" >> \"\$LOG_FILE\"")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -543,4 +543,34 @@
|
|||||||
<!-- SuSFS 自启动相关字符串 -->
|
<!-- SuSFS 自启动相关字符串 -->
|
||||||
<string name="susfs_autostart_enabled_success">SuSFS自启动模块已启用,模块路径:%s</string>
|
<string name="susfs_autostart_enabled_success">SuSFS自启动模块已启用,模块路径:%s</string>
|
||||||
<string name="susfs_autostart_disabled_success">SuSFS自启动模块已禁用</string>
|
<string name="susfs_autostart_disabled_success">SuSFS自启动模块已禁用</string>
|
||||||
|
<string name="susfs_execute_location_updated_post_fs_data">执行位置已更新到 post-fs-data</string>
|
||||||
|
<string name="susfs_execute_location_updated_service">执行位置已更新到 service</string>
|
||||||
|
<string name="susfs_execute_location_update_failed">执行位置更新失败</string>
|
||||||
|
<string name="susfs_execute_location_update_error">更新执行位置时出错:%s</string>
|
||||||
|
<!-- SuSFS Kstat相关字符串 -->
|
||||||
|
<string name="susfs_tab_kstat_config">Kstat 配置</string>
|
||||||
|
<string name="kstat_static_config_added">Kstat静态配置已添加: %1$s</string>
|
||||||
|
<string name="kstat_config_removed">已移除Kstat配置: %1$s</string>
|
||||||
|
<string name="kstat_path_added">Kstat路径已添加: %1$s</string>
|
||||||
|
<string name="kstat_path_removed">已移除Kstat路径: %1$s</string>
|
||||||
|
<string name="kstat_updated">Kstat已更新: %1$s</string>
|
||||||
|
<string name="kstat_full_clone_updated">Kstat完整克隆已更新: %1$s</string>
|
||||||
|
<string name="add_kstat_statically_title">添加Kstat静态配置</string>
|
||||||
|
<string name="file_or_directory_path_label">文件/目录路径</string>
|
||||||
|
<string name="hint_use_default_value">提示:可以使用 ”default“ 来使用原始值</string>
|
||||||
|
<string name="add_kstat_path_title">添加Kstat路径</string>
|
||||||
|
<string name="kstat_command_description">此命令用于在路径被绑定挂载或覆盖之前添加,用于在内核内存中存储原始stat信息。</string>
|
||||||
|
<string name="add">添加</string>
|
||||||
|
<string name="reset_kstat_config_title">重置Kstat配置</string>
|
||||||
|
<string name="reset_kstat_config_message">确定要清除所有Kstat配置吗?此操作不可撤销。</string>
|
||||||
|
<string name="confirm_reset">确认重置</string>
|
||||||
|
<string name="kstat_config_management">Kstat配置管理</string>
|
||||||
|
<string name="kstat_config_description_title">Kstat配置说明</string>
|
||||||
|
<string name="kstat_config_description_add_statically">• add_sus_kstat_statically: 静态配置文件/目录的stat信息</string>
|
||||||
|
<string name="kstat_config_description_add">• add_sus_kstat: 在绑定挂载前添加路径,存储原始stat信息</string>
|
||||||
|
<string name="kstat_config_description_update">• update_sus_kstat: 更新目标ino,保持size和blocks不变</string>
|
||||||
|
<string name="kstat_config_description_update_full_clone">• update_sus_kstat_full_clone: 仅更新ino,其他保持原始值</string>
|
||||||
|
<string name="static_kstat_config">静态Kstat配置</string>
|
||||||
|
<string name="kstat_path_management">Kstat路径管理</string>
|
||||||
|
<string name="no_kstat_config_message">暂无Kstat配置,点击上方按钮添加配置</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -545,4 +545,34 @@
|
|||||||
<!-- SuSFS 自启动相关字符串 -->
|
<!-- SuSFS 自启动相关字符串 -->
|
||||||
<string name="susfs_autostart_enabled_success">SuSFS auto-start module enabled, module path: %s</string>
|
<string name="susfs_autostart_enabled_success">SuSFS auto-start module enabled, module path: %s</string>
|
||||||
<string name="susfs_autostart_disabled_success">SuSFS auto-start module disabled</string>
|
<string name="susfs_autostart_disabled_success">SuSFS auto-start module disabled</string>
|
||||||
|
<string name="susfs_execute_location_updated_post_fs_data">Execution location updated to post-fs-data</string>
|
||||||
|
<string name="susfs_execute_location_updated_service">Execution location updated to service</string>
|
||||||
|
<string name="susfs_execute_location_update_failed">Failed to update execution location</string>
|
||||||
|
<string name="susfs_execute_location_update_error">Error updating execution location: %s</string>
|
||||||
|
<!-- SuSFS Kstat相关字符串 -->
|
||||||
|
<string name="susfs_tab_kstat_config">Kstat Configuration</string>
|
||||||
|
<string name="kstat_static_config_added">Kstat static configuration added: %1$s</string>
|
||||||
|
<string name="kstat_config_removed">Kstat configuration removed: %1$s</string>
|
||||||
|
<string name="kstat_path_added">Kstat path added: %1$s</string>
|
||||||
|
<string name="kstat_path_removed">Kstat path removed: %1$s</string>
|
||||||
|
<string name="kstat_updated">Kstat updated: %1$s</string>
|
||||||
|
<string name="kstat_full_clone_updated">Kstat full clone updated: %1$s</string>
|
||||||
|
<string name="add_kstat_statically_title">Add Kstat Static Configuration</string>
|
||||||
|
<string name="file_or_directory_path_label">File/Directory Path</string>
|
||||||
|
<string name="hint_use_default_value">Hint: You can use ”default“ to use the original value</string>
|
||||||
|
<string name="add_kstat_path_title">Add Kstat Path</string>
|
||||||
|
<string name="kstat_command_description">This command is used to add before the path is bind-mounted or overlaid, storing the original stat information in kernel memory.</string>
|
||||||
|
<string name="add">Add</string>
|
||||||
|
<string name="reset_kstat_config_title">Reset Kstat Configuration</string>
|
||||||
|
<string name="reset_kstat_config_message">Are you sure you want to clear all Kstat configurations? This action cannot be undone.</string>
|
||||||
|
<string name="confirm_reset">Confirm Reset</string>
|
||||||
|
<string name="kstat_config_management">Kstat Configuration Management</string>
|
||||||
|
<string name="kstat_config_description_title">Kstat Configuration Description</string>
|
||||||
|
<string name="kstat_config_description_add_statically">• add_sus_kstat_statically: Static stat info of files/directories</string>
|
||||||
|
<string name="kstat_config_description_add">• add_sus_kstat: Add path before bind mount, storing original stat info</string>
|
||||||
|
<string name="kstat_config_description_update">• update_sus_kstat: Update target ino, keep size and blocks unchanged</string>
|
||||||
|
<string name="kstat_config_description_update_full_clone">• update_sus_kstat_full_clone: Update ino only, keep other original values</string>
|
||||||
|
<string name="static_kstat_config">Static Kstat Configuration</string>
|
||||||
|
<string name="kstat_path_management">Kstat Path Management</string>
|
||||||
|
<string name="no_kstat_config_message">No Kstat configuration yet, click the button above to add</string>
|
||||||
</resources>
|
</resources>
|
||||||
Reference in New Issue
Block a user