manager: Provides re-editable functionality for all SuSFS path configurations

This commit is contained in:
ShirkNeko
2025-06-29 20:36:45 +08:00
parent 4a1ab76322
commit 3551441e42
7 changed files with 348 additions and 52 deletions

View File

@@ -20,6 +20,7 @@ import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TextButton import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
@@ -42,10 +43,18 @@ fun AddPathDialog(
isLoading: Boolean, isLoading: Boolean,
titleRes: Int, titleRes: Int,
labelRes: Int, labelRes: Int,
placeholderRes: Int placeholderRes: Int,
initialValue: String = ""
) { ) {
var newPath by remember { mutableStateOf("") } var newPath by remember { mutableStateOf("") }
// 当对话框显示时,设置初始值
LaunchedEffect(showDialog, initialValue) {
if (showDialog) {
newPath = initialValue
}
}
if (showDialog) { if (showDialog) {
AlertDialog( AlertDialog(
onDismissRequest = onDismiss, onDismissRequest = onDismiss,
@@ -77,7 +86,7 @@ fun AddPathDialog(
enabled = newPath.isNotBlank() && !isLoading, enabled = newPath.isNotBlank() && !isLoading,
shape = RoundedCornerShape(8.dp) shape = RoundedCornerShape(8.dp)
) { ) {
Text(stringResource(R.string.add)) Text(stringResource(if (initialValue.isNotEmpty()) R.string.susfs_save else R.string.add))
} }
}, },
dismissButton = { dismissButton = {
@@ -105,18 +114,28 @@ fun AddTryUmountDialog(
showDialog: Boolean, showDialog: Boolean,
onDismiss: () -> Unit, onDismiss: () -> Unit,
onConfirm: (String, Int) -> Unit, onConfirm: (String, Int) -> Unit,
isLoading: Boolean isLoading: Boolean,
initialPath: String = "",
initialMode: Int = 0
) { ) {
var newUmountPath by remember { mutableStateOf("") } var newUmountPath by remember { mutableStateOf("") }
var newUmountMode by remember { mutableIntStateOf(0) } var newUmountMode by remember { mutableIntStateOf(0) }
var umountModeExpanded by remember { mutableStateOf(false) } var umountModeExpanded by remember { mutableStateOf(false) }
// 当对话框显示时,设置初始值
LaunchedEffect(showDialog, initialPath, initialMode) {
if (showDialog) {
newUmountPath = initialPath
newUmountMode = initialMode
}
}
if (showDialog) { if (showDialog) {
AlertDialog( AlertDialog(
onDismissRequest = onDismiss, onDismissRequest = onDismiss,
title = { title = {
Text( Text(
stringResource(R.string.susfs_add_try_umount), stringResource(if (initialPath.isNotEmpty()) R.string.susfs_edit_try_umount else R.string.susfs_add_try_umount),
style = MaterialTheme.typography.titleLarge, style = MaterialTheme.typography.titleLarge,
fontWeight = FontWeight.Bold fontWeight = FontWeight.Bold
) )
@@ -186,7 +205,7 @@ fun AddTryUmountDialog(
enabled = newUmountPath.isNotBlank() && !isLoading, enabled = newUmountPath.isNotBlank() && !isLoading,
shape = RoundedCornerShape(8.dp) shape = RoundedCornerShape(8.dp)
) { ) {
Text(stringResource(R.string.add)) Text(stringResource(if (initialPath.isNotEmpty()) R.string.susfs_save else R.string.add))
} }
}, },
dismissButton = { dismissButton = {
@@ -214,7 +233,8 @@ fun AddKstatStaticallyDialog(
showDialog: Boolean, showDialog: Boolean,
onDismiss: () -> Unit, onDismiss: () -> Unit,
onConfirm: (String, String, String, String, String, String, String, String, String, String, String, String, String) -> Unit, onConfirm: (String, String, String, String, String, String, String, String, String, String, String, String, String) -> Unit,
isLoading: Boolean isLoading: Boolean,
initialConfig: String = ""
) { ) {
var newKstatPath by remember { mutableStateOf("") } var newKstatPath by remember { mutableStateOf("") }
var newKstatIno by remember { mutableStateOf("") } var newKstatIno by remember { mutableStateOf("") }
@@ -230,12 +250,49 @@ fun AddKstatStaticallyDialog(
var newKstatBlocks by remember { mutableStateOf("") } var newKstatBlocks by remember { mutableStateOf("") }
var newKstatBlksize by remember { mutableStateOf("") } var newKstatBlksize by remember { mutableStateOf("") }
// 当对话框显示时,解析初始配置
LaunchedEffect(showDialog, initialConfig) {
if (showDialog && initialConfig.isNotEmpty()) {
val parts = initialConfig.split("|")
if (parts.size >= 13) {
newKstatPath = parts[0]
newKstatIno = if (parts[1] == "default") "" else parts[1]
newKstatDev = if (parts[2] == "default") "" else parts[2]
newKstatNlink = if (parts[3] == "default") "" else parts[3]
newKstatSize = if (parts[4] == "default") "" else parts[4]
newKstatAtime = if (parts[5] == "default") "" else parts[5]
newKstatAtimeNsec = if (parts[6] == "default") "" else parts[6]
newKstatMtime = if (parts[7] == "default") "" else parts[7]
newKstatMtimeNsec = if (parts[8] == "default") "" else parts[8]
newKstatCtime = if (parts[9] == "default") "" else parts[9]
newKstatCtimeNsec = if (parts[10] == "default") "" else parts[10]
newKstatBlocks = if (parts[11] == "default") "" else parts[11]
newKstatBlksize = if (parts[12] == "default") "" else parts[12]
}
} else if (showDialog && initialConfig.isEmpty()) {
// 清空所有字段
newKstatPath = ""
newKstatIno = ""
newKstatDev = ""
newKstatNlink = ""
newKstatSize = ""
newKstatAtime = ""
newKstatAtimeNsec = ""
newKstatMtime = ""
newKstatMtimeNsec = ""
newKstatCtime = ""
newKstatCtimeNsec = ""
newKstatBlocks = ""
newKstatBlksize = ""
}
}
if (showDialog) { if (showDialog) {
AlertDialog( AlertDialog(
onDismissRequest = onDismiss, onDismissRequest = onDismiss,
title = { title = {
Text( Text(
stringResource(R.string.add_kstat_statically_title), stringResource(if (initialConfig.isNotEmpty()) R.string.edit_kstat_statically_title else R.string.add_kstat_statically_title),
style = MaterialTheme.typography.titleLarge, style = MaterialTheme.typography.titleLarge,
fontWeight = FontWeight.Bold fontWeight = FontWeight.Bold
) )
@@ -431,7 +488,7 @@ fun AddKstatStaticallyDialog(
enabled = newKstatPath.isNotBlank() && !isLoading, enabled = newKstatPath.isNotBlank() && !isLoading,
shape = RoundedCornerShape(8.dp) shape = RoundedCornerShape(8.dp)
) { ) {
Text(stringResource(R.string.add)) Text(stringResource(if (initialConfig.isNotEmpty()) R.string.susfs_save else R.string.add))
} }
}, },
dismissButton = { dismissButton = {

View File

@@ -51,7 +51,8 @@ fun SusPathsContent(
susPaths: Set<String>, susPaths: Set<String>,
isLoading: Boolean, isLoading: Boolean,
onAddPath: () -> Unit, onAddPath: () -> Unit,
onRemovePath: (String) -> Unit onRemovePath: (String) -> Unit,
onEditPath: ((String) -> Unit)? = null
) { ) {
Box(modifier = Modifier.fillMaxSize()) { Box(modifier = Modifier.fillMaxSize()) {
LazyColumn( LazyColumn(
@@ -70,6 +71,7 @@ fun SusPathsContent(
path = path, path = path,
icon = Icons.Default.Folder, icon = Icons.Default.Folder,
onDelete = { onRemovePath(path) }, onDelete = { onRemovePath(path) },
onEdit = if (onEditPath != null) { { onEditPath(path) } } else null,
isLoading = isLoading isLoading = isLoading
) )
} }
@@ -116,6 +118,7 @@ fun SusMountsContent(
isLoading: Boolean, isLoading: Boolean,
onAddMount: () -> Unit, onAddMount: () -> Unit,
onRemoveMount: (String) -> Unit, onRemoveMount: (String) -> Unit,
onEditMount: ((String) -> Unit)? = null,
onToggleHideSusMountsForAllProcs: (Boolean) -> Unit onToggleHideSusMountsForAllProcs: (Boolean) -> Unit
) { ) {
Box(modifier = Modifier.fillMaxSize()) { Box(modifier = Modifier.fillMaxSize()) {
@@ -145,6 +148,7 @@ fun SusMountsContent(
path = mount, path = mount,
icon = Icons.Default.Storage, icon = Icons.Default.Storage,
onDelete = { onRemoveMount(mount) }, onDelete = { onRemoveMount(mount) },
onEdit = if (onEditMount != null) { { onEditMount(mount) } } else null,
isLoading = isLoading isLoading = isLoading
) )
} }
@@ -189,7 +193,8 @@ fun TryUmountContent(
isLoading: Boolean, isLoading: Boolean,
onAddUmount: () -> Unit, onAddUmount: () -> Unit,
onRunUmount: () -> Unit, onRunUmount: () -> Unit,
onRemoveUmount: (String) -> Unit onRemoveUmount: (String) -> Unit,
onEditUmount: ((String) -> Unit)? = null
) { ) {
Box(modifier = Modifier.fillMaxSize()) { Box(modifier = Modifier.fillMaxSize()) {
LazyColumn( LazyColumn(
@@ -218,6 +223,7 @@ fun TryUmountContent(
icon = Icons.Default.Storage, icon = Icons.Default.Storage,
additionalInfo = stringResource(R.string.susfs_umount_mode_display, modeText, mode), additionalInfo = stringResource(R.string.susfs_umount_mode_display, modeText, mode),
onDelete = { onRemoveUmount(umountEntry) }, onDelete = { onRemoveUmount(umountEntry) },
onEdit = if (onEditUmount != null) { { onEditUmount(umountEntry) } } else null,
isLoading = isLoading isLoading = isLoading
) )
} }
@@ -282,7 +288,9 @@ fun KstatConfigContent(
onAddKstatStatically: () -> Unit, onAddKstatStatically: () -> Unit,
onAddKstat: () -> Unit, onAddKstat: () -> Unit,
onRemoveKstatConfig: (String) -> Unit, onRemoveKstatConfig: (String) -> Unit,
onEditKstatConfig: ((String) -> Unit)? = null,
onRemoveAddKstat: (String) -> Unit, onRemoveAddKstat: (String) -> Unit,
onEditAddKstat: ((String) -> Unit)? = null,
onUpdateKstat: (String) -> Unit, onUpdateKstat: (String) -> Unit,
onUpdateKstatFullClone: (String) -> Unit onUpdateKstatFullClone: (String) -> Unit
) { ) {
@@ -347,6 +355,7 @@ fun KstatConfigContent(
KstatConfigItemCard( KstatConfigItemCard(
config = config, config = config,
onDelete = { onRemoveKstatConfig(config) }, onDelete = { onRemoveKstatConfig(config) },
onEdit = if (onEditKstatConfig != null) { { onEditKstatConfig(config) } } else null,
isLoading = isLoading isLoading = isLoading
) )
} }
@@ -365,6 +374,7 @@ fun KstatConfigContent(
AddKstatPathItemCard( AddKstatPathItemCard(
path = path, path = path,
onDelete = { onRemoveAddKstat(path) }, onDelete = { onRemoveAddKstat(path) },
onEdit = if (onEditAddKstat != null) { { onEditAddKstat(path) } } else null,
onUpdate = { onUpdateKstat(path) }, onUpdate = { onUpdateKstat(path) },
onUpdateFullClone = { onUpdateKstatFullClone(path) }, onUpdateFullClone = { onUpdateKstatFullClone(path) },
isLoading = isLoading isLoading = isLoading

View File

@@ -164,6 +164,13 @@ fun SuSFSConfigScreen(
var showAddKstatStaticallyDialog by remember { mutableStateOf(false) } var showAddKstatStaticallyDialog by remember { mutableStateOf(false) }
var showAddKstatDialog by remember { mutableStateOf(false) } var showAddKstatDialog by remember { mutableStateOf(false) }
// 编辑状态
var editingPath by remember { mutableStateOf<String?>(null) }
var editingMount by remember { mutableStateOf<String?>(null) }
var editingUmount by remember { mutableStateOf<String?>(null) }
var editingKstatConfig by remember { mutableStateOf<String?>(null) }
var editingKstatPath by remember { mutableStateOf<String?>(null) }
// 重置确认对话框状态 // 重置确认对话框状态
var showResetPathsDialog by remember { mutableStateOf(false) } var showResetPathsDialog by remember { mutableStateOf(false) }
var showResetMountsDialog by remember { mutableStateOf(false) } var showResetMountsDialog by remember { mutableStateOf(false) }
@@ -499,94 +506,148 @@ fun SuSFSConfigScreen(
// 各种对话框 // 各种对话框
AddPathDialog( AddPathDialog(
showDialog = showAddPathDialog, showDialog = showAddPathDialog,
onDismiss = { showAddPathDialog = false }, onDismiss = {
showAddPathDialog = false
editingPath = null
},
onConfirm = { path -> onConfirm = { path ->
coroutineScope.launch { coroutineScope.launch {
isLoading = true isLoading = true
if (SuSFSManager.addSusPath(context, path)) { val success = if (editingPath != null) {
SuSFSManager.editSusPath(context, editingPath!!, path)
} else {
SuSFSManager.addSusPath(context, path)
}
if (success) {
susPaths = SuSFSManager.getSusPaths(context) susPaths = SuSFSManager.getSusPaths(context)
} }
isLoading = false isLoading = false
showAddPathDialog = false showAddPathDialog = false
editingPath = null
} }
}, },
isLoading = isLoading, isLoading = isLoading,
titleRes = R.string.susfs_add_sus_path, titleRes = if (editingPath != null) R.string.susfs_edit_sus_path else R.string.susfs_add_sus_path,
labelRes = R.string.susfs_path_label, labelRes = R.string.susfs_path_label,
placeholderRes = R.string.susfs_path_placeholder placeholderRes = R.string.susfs_path_placeholder,
initialValue = editingPath ?: ""
) )
AddPathDialog( AddPathDialog(
showDialog = showAddMountDialog, showDialog = showAddMountDialog,
onDismiss = { showAddMountDialog = false }, onDismiss = {
showAddMountDialog = false
editingMount = null
},
onConfirm = { mount -> onConfirm = { mount ->
coroutineScope.launch { coroutineScope.launch {
isLoading = true isLoading = true
if (SuSFSManager.addSusMount(context, mount)) { val success = if (editingMount != null) {
SuSFSManager.editSusMount(context, editingMount!!, mount)
} else {
SuSFSManager.addSusMount(context, mount)
}
if (success) {
susMounts = SuSFSManager.getSusMounts(context) susMounts = SuSFSManager.getSusMounts(context)
} }
isLoading = false isLoading = false
showAddMountDialog = false showAddMountDialog = false
editingMount = null
} }
}, },
isLoading = isLoading, isLoading = isLoading,
titleRes = R.string.susfs_add_sus_mount, titleRes = if (editingMount != null) R.string.susfs_edit_sus_mount else R.string.susfs_add_sus_mount,
labelRes = R.string.susfs_mount_path_label, labelRes = R.string.susfs_mount_path_label,
placeholderRes = R.string.susfs_path_placeholder placeholderRes = R.string.susfs_path_placeholder,
initialValue = editingMount ?: ""
) )
AddTryUmountDialog( AddTryUmountDialog(
showDialog = showAddUmountDialog, showDialog = showAddUmountDialog,
onDismiss = { showAddUmountDialog = false }, onDismiss = {
showAddUmountDialog = false
editingUmount = null
},
onConfirm = { path, mode -> onConfirm = { path, mode ->
coroutineScope.launch { coroutineScope.launch {
isLoading = true isLoading = true
if (SuSFSManager.addTryUmount(context, path, mode)) { val success = if (editingUmount != null) {
SuSFSManager.editTryUmount(context, editingUmount!!, path, mode)
} else {
SuSFSManager.addTryUmount(context, path, mode)
}
if (success) {
tryUmounts = SuSFSManager.getTryUmounts(context) tryUmounts = SuSFSManager.getTryUmounts(context)
} }
isLoading = false isLoading = false
showAddUmountDialog = false showAddUmountDialog = false
editingUmount = null
} }
}, },
isLoading = isLoading isLoading = isLoading,
initialPath = editingUmount?.split("|")?.get(0) ?: "",
initialMode = editingUmount?.split("|")?.get(1)?.toIntOrNull() ?: 0
) )
AddKstatStaticallyDialog( AddKstatStaticallyDialog(
showDialog = showAddKstatStaticallyDialog, showDialog = showAddKstatStaticallyDialog,
onDismiss = { showAddKstatStaticallyDialog = false }, onDismiss = {
showAddKstatStaticallyDialog = false
editingKstatConfig = null
},
onConfirm = { path, ino, dev, nlink, size, atime, atimeNsec, mtime, mtimeNsec, ctime, ctimeNsec, blocks, blksize -> onConfirm = { path, ino, dev, nlink, size, atime, atimeNsec, mtime, mtimeNsec, ctime, ctimeNsec, blocks, blksize ->
coroutineScope.launch { coroutineScope.launch {
isLoading = true isLoading = true
if (SuSFSManager.addKstatStatically( val success = if (editingKstatConfig != null) {
SuSFSManager.editKstatConfig(
context, editingKstatConfig!!, path, ino, dev, nlink, size, atime, atimeNsec,
mtime, mtimeNsec, ctime, ctimeNsec, blocks, blksize
)
} else {
SuSFSManager.addKstatStatically(
context, path, ino, dev, nlink, size, atime, atimeNsec, context, path, ino, dev, nlink, size, atime, atimeNsec,
mtime, mtimeNsec, ctime, ctimeNsec, blocks, blksize mtime, mtimeNsec, ctime, ctimeNsec, blocks, blksize
)) { )
}
if (success) {
kstatConfigs = SuSFSManager.getKstatConfigs(context) kstatConfigs = SuSFSManager.getKstatConfigs(context)
} }
isLoading = false isLoading = false
showAddKstatStaticallyDialog = false showAddKstatStaticallyDialog = false
editingKstatConfig = null
} }
}, },
isLoading = isLoading isLoading = isLoading,
initialConfig = editingKstatConfig ?: ""
) )
AddPathDialog( AddPathDialog(
showDialog = showAddKstatDialog, showDialog = showAddKstatDialog,
onDismiss = { showAddKstatDialog = false }, onDismiss = {
showAddKstatDialog = false
editingKstatPath = null
},
onConfirm = { path -> onConfirm = { path ->
coroutineScope.launch { coroutineScope.launch {
isLoading = true isLoading = true
if (SuSFSManager.addKstat(context, path)) { val success = if (editingKstatPath != null) {
SuSFSManager.editAddKstat(context, editingKstatPath!!, path)
} else {
SuSFSManager.addKstat(context, path)
}
if (success) {
addKstatPaths = SuSFSManager.getAddKstatPaths(context) addKstatPaths = SuSFSManager.getAddKstatPaths(context)
} }
isLoading = false isLoading = false
showAddKstatDialog = false showAddKstatDialog = false
editingKstatPath = null
} }
}, },
isLoading = isLoading, isLoading = isLoading,
titleRes = R.string.add_kstat_path_title, titleRes = if (editingKstatPath != null) R.string.edit_kstat_path_title else R.string.add_kstat_path_title,
labelRes = R.string.file_or_directory_path_label, labelRes = R.string.file_or_directory_path_label,
placeholderRes = R.string.susfs_path_placeholder placeholderRes = R.string.susfs_path_placeholder,
initialValue = editingKstatPath ?: ""
) )
// 确认对话框 // 确认对话框
@@ -1066,6 +1127,10 @@ fun SuSFSConfigScreen(
} }
isLoading = false isLoading = false
} }
},
onEditPath = { path ->
editingPath = path
showAddPathDialog = true
} }
) )
} }
@@ -1087,6 +1152,10 @@ fun SuSFSConfigScreen(
isLoading = false isLoading = false
} }
}, },
onEditMount = { mount ->
editingMount = mount
showAddMountDialog = true
},
onToggleHideSusMountsForAllProcs = { hideForAll -> onToggleHideSusMountsForAllProcs = { hideForAll ->
coroutineScope.launch { coroutineScope.launch {
isLoading = true isLoading = true
@@ -1113,6 +1182,10 @@ fun SuSFSConfigScreen(
} }
isLoading = false isLoading = false
} }
},
onEditUmount = { umountEntry ->
editingUmount = umountEntry
showAddUmountDialog = true
} }
) )
} }
@@ -1132,6 +1205,10 @@ fun SuSFSConfigScreen(
isLoading = false isLoading = false
} }
}, },
onEditKstatConfig = { config ->
editingKstatConfig = config
showAddKstatStaticallyDialog = true
},
onRemoveAddKstat = { path -> onRemoveAddKstat = { path ->
coroutineScope.launch { coroutineScope.launch {
isLoading = true isLoading = true
@@ -1141,6 +1218,10 @@ fun SuSFSConfigScreen(
isLoading = false isLoading = false
} }
}, },
onEditAddKstat = { path ->
editingKstatPath = path
showAddKstatDialog = true
},
onUpdateKstat = { path -> onUpdateKstat = { path ->
coroutineScope.launch { coroutineScope.launch {
isLoading = true isLoading = true

View File

@@ -14,6 +14,7 @@ import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Delete import androidx.compose.material.icons.filled.Delete
import androidx.compose.material.icons.filled.Edit
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.Update import androidx.compose.material.icons.filled.Update
@@ -91,6 +92,7 @@ fun PathItemCard(
path: String, path: String,
icon: ImageVector, icon: ImageVector,
onDelete: () -> Unit, onDelete: () -> Unit,
onEdit: (() -> Unit)? = null,
isLoading: Boolean = false, isLoading: Boolean = false,
additionalInfo: String? = null additionalInfo: String? = null
) { ) {
@@ -134,6 +136,23 @@ fun PathItemCard(
} }
} }
} }
Row(
horizontalArrangement = Arrangement.spacedBy(4.dp)
) {
if (onEdit != null) {
IconButton(
onClick = onEdit,
enabled = !isLoading,
modifier = Modifier.size(32.dp)
) {
Icon(
imageVector = Icons.Default.Edit,
contentDescription = stringResource(R.string.edit),
tint = MaterialTheme.colorScheme.secondary,
modifier = Modifier.size(16.dp)
)
}
}
IconButton( IconButton(
onClick = onDelete, onClick = onDelete,
enabled = !isLoading, enabled = !isLoading,
@@ -141,7 +160,7 @@ fun PathItemCard(
) { ) {
Icon( Icon(
imageVector = Icons.Default.Delete, imageVector = Icons.Default.Delete,
contentDescription = null, contentDescription = stringResource(R.string.delete),
tint = MaterialTheme.colorScheme.error, tint = MaterialTheme.colorScheme.error,
modifier = Modifier.size(16.dp) modifier = Modifier.size(16.dp)
) )
@@ -149,6 +168,7 @@ fun PathItemCard(
} }
} }
} }
}
/** /**
* Kstat配置项目卡片组件 * Kstat配置项目卡片组件
@@ -157,6 +177,7 @@ fun PathItemCard(
fun KstatConfigItemCard( fun KstatConfigItemCard(
config: String, config: String,
onDelete: () -> Unit, onDelete: () -> Unit,
onEdit: (() -> Unit)? = null,
isLoading: Boolean = false isLoading: Boolean = false
) { ) {
Card( Card(
@@ -208,6 +229,23 @@ fun KstatConfigItemCard(
} }
} }
} }
Row(
horizontalArrangement = Arrangement.spacedBy(4.dp)
) {
if (onEdit != null) {
IconButton(
onClick = onEdit,
enabled = !isLoading,
modifier = Modifier.size(32.dp)
) {
Icon(
imageVector = Icons.Default.Edit,
contentDescription = stringResource(R.string.edit),
tint = MaterialTheme.colorScheme.secondary,
modifier = Modifier.size(16.dp)
)
}
}
IconButton( IconButton(
onClick = onDelete, onClick = onDelete,
enabled = !isLoading, enabled = !isLoading,
@@ -215,7 +253,7 @@ fun KstatConfigItemCard(
) { ) {
Icon( Icon(
imageVector = Icons.Default.Delete, imageVector = Icons.Default.Delete,
contentDescription = null, contentDescription = stringResource(R.string.delete),
tint = MaterialTheme.colorScheme.error, tint = MaterialTheme.colorScheme.error,
modifier = Modifier.size(16.dp) modifier = Modifier.size(16.dp)
) )
@@ -223,6 +261,7 @@ fun KstatConfigItemCard(
} }
} }
} }
}
/** /**
* Add Kstat路径项目卡片组件 * Add Kstat路径项目卡片组件
@@ -231,6 +270,7 @@ fun KstatConfigItemCard(
fun AddKstatPathItemCard( fun AddKstatPathItemCard(
path: String, path: String,
onDelete: () -> Unit, onDelete: () -> Unit,
onEdit: (() -> Unit)? = null,
onUpdate: () -> Unit, onUpdate: () -> Unit,
onUpdateFullClone: () -> Unit, onUpdateFullClone: () -> Unit,
isLoading: Boolean = false isLoading: Boolean = false
@@ -269,6 +309,20 @@ fun AddKstatPathItemCard(
Row( Row(
horizontalArrangement = Arrangement.spacedBy(4.dp) horizontalArrangement = Arrangement.spacedBy(4.dp)
) { ) {
if (onEdit != null) {
IconButton(
onClick = onEdit,
enabled = !isLoading,
modifier = Modifier.size(32.dp)
) {
Icon(
imageVector = Icons.Default.Edit,
contentDescription = stringResource(R.string.edit),
tint = MaterialTheme.colorScheme.secondary,
modifier = Modifier.size(16.dp)
)
}
}
IconButton( IconButton(
onClick = onUpdate, onClick = onUpdate,
enabled = !isLoading, enabled = !isLoading,
@@ -276,7 +330,7 @@ fun AddKstatPathItemCard(
) { ) {
Icon( Icon(
imageVector = Icons.Default.Update, imageVector = Icons.Default.Update,
contentDescription = null, contentDescription = stringResource(R.string.update),
tint = MaterialTheme.colorScheme.secondary, tint = MaterialTheme.colorScheme.secondary,
modifier = Modifier.size(16.dp) modifier = Modifier.size(16.dp)
) )
@@ -288,7 +342,7 @@ fun AddKstatPathItemCard(
) { ) {
Icon( Icon(
imageVector = Icons.Default.PlayArrow, imageVector = Icons.Default.PlayArrow,
contentDescription = null, contentDescription = stringResource(R.string.susfs_update_full_clone),
tint = MaterialTheme.colorScheme.tertiary, tint = MaterialTheme.colorScheme.tertiary,
modifier = Modifier.size(16.dp) modifier = Modifier.size(16.dp)
) )
@@ -300,7 +354,7 @@ fun AddKstatPathItemCard(
) { ) {
Icon( Icon(
imageVector = Icons.Default.Delete, imageVector = Icons.Default.Delete,
contentDescription = null, contentDescription = stringResource(R.string.delete),
tint = MaterialTheme.colorScheme.error, tint = MaterialTheme.colorScheme.error,
modifier = Modifier.size(16.dp) modifier = Modifier.size(16.dp)
) )

View File

@@ -800,6 +800,19 @@ object SuSFSManager {
return true return true
} }
// 编辑SUS路径
suspend fun editSusPath(context: Context, oldPath: String, newPath: String): Boolean {
val currentPaths = getSusPaths(context).toMutableSet()
if (currentPaths.remove(oldPath)) {
currentPaths.add(newPath)
saveSusPaths(context, currentPaths)
if (isAutoStartEnabled(context)) updateMagiskModule(context)
showToast(context, "SUS path updated: $oldPath -> $newPath")
return true
}
return false
}
// 添加SUS挂载 // 添加SUS挂载
suspend fun addSusMount(context: Context, mount: String): Boolean { suspend fun addSusMount(context: Context, mount: String): Boolean {
val success = executeSusfsCommand(context, "add_sus_mount '$mount'") val success = executeSusfsCommand(context, "add_sus_mount '$mount'")
@@ -817,6 +830,19 @@ object SuSFSManager {
return true return true
} }
// 编辑SUS挂载
suspend fun editSusMount(context: Context, oldMount: String, newMount: String): Boolean {
val currentMounts = getSusMounts(context).toMutableSet()
if (currentMounts.remove(oldMount)) {
currentMounts.add(newMount)
saveSusMounts(context, currentMounts)
if (isAutoStartEnabled(context)) updateMagiskModule(context)
showToast(context, "SUS mount updated: $oldMount -> $newMount")
return true
}
return false
}
// 添加尝试卸载 // 添加尝试卸载
suspend fun addTryUmount(context: Context, path: String, mode: Int): Boolean { suspend fun addTryUmount(context: Context, path: String, mode: Int): Boolean {
val commandSuccess = executeSusfsCommand(context, "add_try_umount '$path' $mode") val commandSuccess = executeSusfsCommand(context, "add_try_umount '$path' $mode")
@@ -839,6 +865,19 @@ object SuSFSManager {
return true return true
} }
// 编辑尝试卸载
suspend fun editTryUmount(context: Context, oldEntry: String, newPath: String, newMode: Int): Boolean {
val currentUmounts = getTryUmounts(context).toMutableSet()
if (currentUmounts.remove(oldEntry)) {
currentUmounts.add("$newPath|$newMode")
saveTryUmounts(context, currentUmounts)
if (isAutoStartEnabled(context)) updateMagiskModule(context)
showToast(context, "Try umount updated: $oldEntry -> $newPath|$newMode")
return true
}
return false
}
suspend fun runTryUmount(context: Context): Boolean = executeSusfsCommand(context, "run_try_umount") suspend fun runTryUmount(context: Context): Boolean = executeSusfsCommand(context, "run_try_umount")
// 添加kstat配置 // 添加kstat配置
@@ -864,6 +903,23 @@ object SuSFSManager {
return true return true
} }
// 编辑kstat配置
@SuppressLint("StringFormatInvalid")
suspend fun editKstatConfig(context: Context, oldConfig: String, 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 currentConfigs = getKstatConfigs(context).toMutableSet()
if (currentConfigs.remove(oldConfig)) {
val newConfigEntry = "$path|$ino|$dev|$nlink|$size|$atime|$atimeNsec|$mtime|$mtimeNsec|$ctime|$ctimeNsec|$blocks|$blksize"
currentConfigs.add(newConfigEntry)
saveKstatConfigs(context, currentConfigs)
if (isAutoStartEnabled(context)) updateMagiskModule(context)
showToast(context, context.getString(R.string.kstat_config_updated, path))
return true
}
return false
}
// 添加kstat路径 // 添加kstat路径
suspend fun addKstat(context: Context, path: String): Boolean { suspend fun addKstat(context: Context, path: String): Boolean {
val success = executeSusfsCommand(context, "add_sus_kstat '$path'") val success = executeSusfsCommand(context, "add_sus_kstat '$path'")
@@ -882,6 +938,20 @@ object SuSFSManager {
return true return true
} }
// 编辑kstat路径
@SuppressLint("StringFormatInvalid")
suspend fun editAddKstat(context: Context, oldPath: String, newPath: String): Boolean {
val currentPaths = getAddKstatPaths(context).toMutableSet()
if (currentPaths.remove(oldPath)) {
currentPaths.add(newPath)
saveAddKstatPaths(context, currentPaths)
if (isAutoStartEnabled(context)) updateMagiskModule(context)
showToast(context, context.getString(R.string.kstat_path_updated, oldPath, newPath))
return true
}
return false
}
// 更新kstat // 更新kstat
suspend fun updateKstat(context: Context, path: String): Boolean { suspend fun updateKstat(context: Context, path: String): Boolean {
val success = executeSusfsCommand(context, "update_sus_kstat '$path'") val success = executeSusfsCommand(context, "update_sus_kstat '$path'")

View File

@@ -541,4 +541,16 @@
<string name="hide_bl_script_description">启用隐藏Bootloader解锁状态脚本</string> <string name="hide_bl_script_description">启用隐藏Bootloader解锁状态脚本</string>
<string name="cleanup_residue">清理工具残留</string> <string name="cleanup_residue">清理工具残留</string>
<string name="cleanup_residue_description">清理各种模块以及工具的残留文件和目录(可能会误删导致丢失以及无法启动,谨慎使用)</string> <string name="cleanup_residue_description">清理各种模块以及工具的残留文件和目录(可能会误删导致丢失以及无法启动,谨慎使用)</string>
<string name="susfs_edit_sus_path">编辑 SUS 路径</string>
<string name="susfs_edit_sus_mount">编辑 SUS 挂载</string>
<string name="susfs_edit_try_umount">编辑尝试卸载</string>
<string name="edit_kstat_statically_title">编辑 Kstat 静态配置</string>
<string name="edit_kstat_path_title">编辑 Kstat 路径</string>
<string name="susfs_save">保存</string>
<string name="edit">编辑</string>
<string name="delete">删除</string>
<string name="update">更新</string>
<string name="kstat_config_updated">Kstat 配置更新</string>
<string name="kstat_path_updated">Kstat 路径更新</string>
<string name="susfs_update_full_clone">Susfs 完整克隆更新</string>
</resources> </resources>

View File

@@ -543,4 +543,16 @@
<string name="hide_bl_script_description">Enable Hide Bootloader Unlock Status Scripts</string> <string name="hide_bl_script_description">Enable Hide Bootloader Unlock Status Scripts</string>
<string name="cleanup_residue">Cleanup Residue</string> <string name="cleanup_residue">Cleanup Residue</string>
<string name="cleanup_residue_description">Clean up the residual files and directories of various modules and tools (may be deleted by mistake, resulting in loss and failure to start, use with caution)</string> <string name="cleanup_residue_description">Clean up the residual files and directories of various modules and tools (may be deleted by mistake, resulting in loss and failure to start, use with caution)</string>
<string name="susfs_edit_sus_path">Edit SUS Path</string>
<string name="susfs_edit_sus_mount">Edit SUS Mount</string>
<string name="susfs_edit_try_umount">Edit Try Umount</string>
<string name="edit_kstat_statically_title">Edit Kstat Static Configuration</string>
<string name="edit_kstat_path_title">Edit Kstat Path</string>
<string name="susfs_save">Save</string>
<string name="edit">Edit</string>
<string name="delete">Delete</string>
<string name="update">Update</string>
<string name="kstat_config_updated">Kstat config update</string>
<string name="kstat_path_updated">Kstat path update</string>
<string name="susfs_update_full_clone">Susfs update full clone</string>
</resources> </resources>