manager:Add SuSFS to obtain slot uname and build time information
This commit is contained in:
@@ -77,6 +77,7 @@ import androidx.lifecycle.viewmodel.compose.viewModel
|
|||||||
import com.ramcosta.composedestinations.annotation.Destination
|
import com.ramcosta.composedestinations.annotation.Destination
|
||||||
import com.ramcosta.composedestinations.annotation.RootGraph
|
import com.ramcosta.composedestinations.annotation.RootGraph
|
||||||
import com.ramcosta.composedestinations.generated.destinations.InstallScreenDestination
|
import com.ramcosta.composedestinations.generated.destinations.InstallScreenDestination
|
||||||
|
import com.ramcosta.composedestinations.generated.destinations.SuSFSConfigScreenDestination
|
||||||
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
||||||
import com.sukisu.ultra.KernelVersion
|
import com.sukisu.ultra.KernelVersion
|
||||||
import com.sukisu.ultra.Natives
|
import com.sukisu.ultra.Natives
|
||||||
@@ -91,6 +92,8 @@ import com.sukisu.ultra.ui.theme.getCardElevation
|
|||||||
import com.sukisu.ultra.ui.util.checkNewVersion
|
import com.sukisu.ultra.ui.util.checkNewVersion
|
||||||
import com.sukisu.ultra.ui.util.module.LatestVersionInfo
|
import com.sukisu.ultra.ui.util.module.LatestVersionInfo
|
||||||
import com.sukisu.ultra.ui.util.reboot
|
import com.sukisu.ultra.ui.util.reboot
|
||||||
|
import com.sukisu.ultra.ui.util.getSuSFS
|
||||||
|
import com.sukisu.ultra.ui.util.SuSFSManager
|
||||||
import com.sukisu.ultra.ui.viewmodel.HomeViewModel
|
import com.sukisu.ultra.ui.viewmodel.HomeViewModel
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
@@ -127,7 +130,8 @@ fun HomeScreen(navigator: DestinationsNavigator) {
|
|||||||
Scaffold(
|
Scaffold(
|
||||||
topBar = {
|
topBar = {
|
||||||
TopBar(
|
TopBar(
|
||||||
scrollBehavior = scrollBehavior
|
scrollBehavior = scrollBehavior,
|
||||||
|
navigator = navigator
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
contentWindowInsets = WindowInsets.safeDrawing.only(
|
contentWindowInsets = WindowInsets.safeDrawing.only(
|
||||||
@@ -259,13 +263,15 @@ fun RebootDropdownItem(@StringRes id: Int, reason: String = "") {
|
|||||||
DropdownMenuItem(
|
DropdownMenuItem(
|
||||||
text = {Text(stringResource(id))},
|
text = {Text(stringResource(id))},
|
||||||
onClick = {reboot(reason)})
|
onClick = {reboot(reason)})
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
private fun TopBar(
|
private fun TopBar(
|
||||||
scrollBehavior: TopAppBarScrollBehavior? = null
|
scrollBehavior: TopAppBarScrollBehavior? = null,
|
||||||
|
navigator: DestinationsNavigator
|
||||||
) {
|
) {
|
||||||
|
val context = LocalContext.current
|
||||||
val colorScheme = MaterialTheme.colorScheme
|
val colorScheme = MaterialTheme.colorScheme
|
||||||
val cardColor = if (CardConfig.isCustomBackgroundEnabled) {
|
val cardColor = if (CardConfig.isCustomBackgroundEnabled) {
|
||||||
colorScheme.surfaceContainerLow
|
colorScheme.surfaceContainerLow
|
||||||
@@ -285,6 +291,19 @@ private fun TopBar(
|
|||||||
scrolledContainerColor = cardColor.copy(alpha = cardAlpha)
|
scrolledContainerColor = cardColor.copy(alpha = cardAlpha)
|
||||||
),
|
),
|
||||||
actions = {
|
actions = {
|
||||||
|
// SuSFS 配置按钮
|
||||||
|
if (getSuSFS() == "Supported" && SuSFSManager.isBinaryAvailable(context)) {
|
||||||
|
IconButton(onClick = {
|
||||||
|
navigator.navigate(SuSFSConfigScreenDestination)
|
||||||
|
}) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Filled.Tune,
|
||||||
|
contentDescription = stringResource(R.string.susfs_config_setting_title)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重启按钮
|
||||||
var showDropdown by remember { mutableStateOf(false) }
|
var showDropdown by remember { mutableStateOf(false) }
|
||||||
KsuIsValid {
|
KsuIsValid {
|
||||||
IconButton(onClick = {
|
IconButton(onClick = {
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import androidx.compose.material.icons.filled.Info
|
|||||||
import androidx.compose.material.icons.filled.Refresh
|
import androidx.compose.material.icons.filled.Refresh
|
||||||
import androidx.compose.material.icons.filled.RestoreFromTrash
|
import androidx.compose.material.icons.filled.RestoreFromTrash
|
||||||
import androidx.compose.material.icons.filled.Settings
|
import androidx.compose.material.icons.filled.Settings
|
||||||
|
import androidx.compose.material.icons.filled.Storage
|
||||||
import androidx.compose.material3.AlertDialog
|
import androidx.compose.material3.AlertDialog
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
import androidx.compose.material3.ButtonDefaults
|
import androidx.compose.material3.ButtonDefaults
|
||||||
@@ -122,6 +123,12 @@ fun SuSFSConfigScreen(
|
|||||||
var lastAppliedBuildTime by remember { mutableStateOf("") }
|
var lastAppliedBuildTime by remember { mutableStateOf("") }
|
||||||
var executeInPostFsData by remember { mutableStateOf(false) } // 是否在post-fs-data中执行
|
var executeInPostFsData by remember { mutableStateOf(false) } // 是否在post-fs-data中执行
|
||||||
|
|
||||||
|
// 槽位信息相关状态
|
||||||
|
var slotInfoList by remember { mutableStateOf(emptyList<SuSFSManager.SlotInfo>()) }
|
||||||
|
var currentActiveSlot by remember { mutableStateOf("") }
|
||||||
|
var isLoadingSlotInfo by remember { mutableStateOf(false) }
|
||||||
|
var showSlotInfoDialog by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
// 路径管理相关状态
|
// 路径管理相关状态
|
||||||
var susPaths by remember { mutableStateOf(emptySet<String>()) }
|
var susPaths by remember { mutableStateOf(emptySet<String>()) }
|
||||||
var susMounts by remember { mutableStateOf(emptySet<String>()) }
|
var susMounts by remember { mutableStateOf(emptySet<String>()) }
|
||||||
@@ -167,6 +174,16 @@ fun SuSFSConfigScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 加载槽位信息
|
||||||
|
fun loadSlotInfo() {
|
||||||
|
coroutineScope.launch {
|
||||||
|
isLoadingSlotInfo = true
|
||||||
|
slotInfoList = SuSFSManager.getCurrentSlotInfo()
|
||||||
|
currentActiveSlot = SuSFSManager.getCurrentActiveSlot()
|
||||||
|
isLoadingSlotInfo = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 加载当前配置
|
// 加载当前配置
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
unameValue = SuSFSManager.getUnameValue(context)
|
unameValue = SuSFSManager.getUnameValue(context)
|
||||||
@@ -180,6 +197,9 @@ 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)
|
||||||
|
|
||||||
|
// 加载槽位信息
|
||||||
|
loadSlotInfo()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 当切换到启用功能状态标签页时加载数据
|
// 当切换到启用功能状态标签页时加载数据
|
||||||
@@ -197,6 +217,153 @@ fun SuSFSConfigScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 槽位信息对话框
|
||||||
|
if (showSlotInfoDialog) {
|
||||||
|
AlertDialog(
|
||||||
|
onDismissRequest = { showSlotInfoDialog = false },
|
||||||
|
title = {
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.susfs_slot_info_title),
|
||||||
|
style = MaterialTheme.typography.titleLarge,
|
||||||
|
fontWeight = FontWeight.Bold
|
||||||
|
)
|
||||||
|
},
|
||||||
|
text = {
|
||||||
|
Column(
|
||||||
|
verticalArrangement = Arrangement.spacedBy(12.dp)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.susfs_current_active_slot, currentActiveSlot),
|
||||||
|
style = MaterialTheme.typography.bodyLarge,
|
||||||
|
fontWeight = FontWeight.Medium,
|
||||||
|
color = MaterialTheme.colorScheme.primary
|
||||||
|
)
|
||||||
|
|
||||||
|
if (slotInfoList.isNotEmpty()) {
|
||||||
|
slotInfoList.forEach { slotInfo ->
|
||||||
|
Card(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
colors = CardDefaults.cardColors(
|
||||||
|
containerColor = if (slotInfo.slotName == currentActiveSlot) {
|
||||||
|
MaterialTheme.colorScheme.primaryContainer.copy(alpha = 0.3f)
|
||||||
|
} else {
|
||||||
|
MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.3f)
|
||||||
|
}
|
||||||
|
),
|
||||||
|
shape = RoundedCornerShape(8.dp)
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.padding(12.dp),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(4.dp)
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.Storage,
|
||||||
|
contentDescription = null,
|
||||||
|
tint = if (slotInfo.slotName == currentActiveSlot) {
|
||||||
|
MaterialTheme.colorScheme.primary
|
||||||
|
} else {
|
||||||
|
MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
|
},
|
||||||
|
modifier = Modifier.size(16.dp)
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.width(6.dp))
|
||||||
|
Text(
|
||||||
|
text = slotInfo.slotName,
|
||||||
|
style = MaterialTheme.typography.titleMedium,
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
color = if (slotInfo.slotName == currentActiveSlot) {
|
||||||
|
MaterialTheme.colorScheme.primary
|
||||||
|
} else {
|
||||||
|
MaterialTheme.colorScheme.onSurface
|
||||||
|
}
|
||||||
|
)
|
||||||
|
if (slotInfo.slotName == currentActiveSlot) {
|
||||||
|
Spacer(modifier = Modifier.width(6.dp))
|
||||||
|
Surface(
|
||||||
|
shape = RoundedCornerShape(4.dp),
|
||||||
|
color = MaterialTheme.colorScheme.primary
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.susfs_slot_current_badge),
|
||||||
|
style = MaterialTheme.typography.labelSmall,
|
||||||
|
color = MaterialTheme.colorScheme.onPrimary,
|
||||||
|
modifier = Modifier.padding(horizontal = 6.dp, vertical = 2.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.susfs_slot_uname, slotInfo.uname),
|
||||||
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.susfs_slot_build_time, slotInfo.buildTime),
|
||||||
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
|
)
|
||||||
|
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
|
) {
|
||||||
|
Button(
|
||||||
|
onClick = {
|
||||||
|
unameValue = slotInfo.uname
|
||||||
|
showSlotInfoDialog = false
|
||||||
|
},
|
||||||
|
modifier = Modifier.weight(1f),
|
||||||
|
shape = RoundedCornerShape(6.dp)
|
||||||
|
) {
|
||||||
|
Text(stringResource(R.string.susfs_slot_use_uname), fontSize = 12.sp)
|
||||||
|
}
|
||||||
|
Button(
|
||||||
|
onClick = {
|
||||||
|
buildTimeValue = slotInfo.buildTime
|
||||||
|
showSlotInfoDialog = false
|
||||||
|
},
|
||||||
|
modifier = Modifier.weight(1f),
|
||||||
|
shape = RoundedCornerShape(6.dp)
|
||||||
|
) {
|
||||||
|
Text(stringResource(R.string.susfs_slot_use_build_time), fontSize = 12.sp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.susfs_slot_info_unavailable),
|
||||||
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
color = MaterialTheme.colorScheme.error
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
confirmButton = {
|
||||||
|
Button(
|
||||||
|
onClick = { loadSlotInfo() },
|
||||||
|
enabled = !isLoadingSlotInfo,
|
||||||
|
shape = RoundedCornerShape(8.dp)
|
||||||
|
) {
|
||||||
|
Text(stringResource(R.string.refresh))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dismissButton = {
|
||||||
|
TextButton(
|
||||||
|
onClick = { showSlotInfoDialog = false },
|
||||||
|
shape = RoundedCornerShape(8.dp)
|
||||||
|
) {
|
||||||
|
Text(stringResource(R.string.close))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
shape = RoundedCornerShape(12.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// 各种对话框的定义保持不变
|
// 各种对话框的定义保持不变
|
||||||
// 添加路径对话框
|
// 添加路径对话框
|
||||||
if (showAddPathDialog) {
|
if (showAddPathDialog) {
|
||||||
@@ -940,6 +1107,7 @@ fun SuSFSConfigScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
onShowSlotInfo = { showSlotInfoDialog = true },
|
||||||
context = context
|
context = context
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -1043,6 +1211,7 @@ private fun BasicSettingsContent(
|
|||||||
canEnableAutoStart: Boolean,
|
canEnableAutoStart: Boolean,
|
||||||
isLoading: Boolean,
|
isLoading: Boolean,
|
||||||
onAutoStartToggle: (Boolean) -> Unit,
|
onAutoStartToggle: (Boolean) -> Unit,
|
||||||
|
onShowSlotInfo: () -> Unit,
|
||||||
context: android.content.Context
|
context: android.content.Context
|
||||||
) {
|
) {
|
||||||
var scriptLocationExpanded by remember { mutableStateOf(false) }
|
var scriptLocationExpanded by remember { mutableStateOf(false) }
|
||||||
@@ -1252,6 +1421,62 @@ private fun BasicSettingsContent(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 槽位信息按钮
|
||||||
|
Card(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
colors = CardDefaults.cardColors(
|
||||||
|
containerColor = MaterialTheme.colorScheme.surface
|
||||||
|
),
|
||||||
|
shape = RoundedCornerShape(12.dp)
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.padding(12.dp),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(12.dp)
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.Info,
|
||||||
|
contentDescription = null,
|
||||||
|
tint = MaterialTheme.colorScheme.primary,
|
||||||
|
modifier = Modifier.size(18.dp)
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.width(8.dp))
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.susfs_slot_info_title),
|
||||||
|
style = MaterialTheme.typography.titleMedium,
|
||||||
|
fontWeight = FontWeight.Medium,
|
||||||
|
color = MaterialTheme.colorScheme.onSurface
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.susfs_slot_info_description),
|
||||||
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
|
lineHeight = 14.sp
|
||||||
|
)
|
||||||
|
|
||||||
|
OutlinedButton(
|
||||||
|
onClick = onShowSlotInfo,
|
||||||
|
enabled = !isLoading,
|
||||||
|
shape = RoundedCornerShape(8.dp),
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.Storage,
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = Modifier.size(16.dp)
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.width(6.dp))
|
||||||
|
Text(
|
||||||
|
stringResource(R.string.susfs_slot_info_title),
|
||||||
|
fontWeight = FontWeight.Medium
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -42,6 +42,15 @@ object SuSFSManager {
|
|||||||
private const val MODULE_ID = "susfs_manager"
|
private const val MODULE_ID = "susfs_manager"
|
||||||
private const val MODULE_PATH = "/data/adb/modules/$MODULE_ID"
|
private const val MODULE_PATH = "/data/adb/modules/$MODULE_ID"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 槽位信息数据类
|
||||||
|
*/
|
||||||
|
data class SlotInfo(
|
||||||
|
val slotName: String,
|
||||||
|
val uname: String,
|
||||||
|
val buildTime: String
|
||||||
|
)
|
||||||
|
|
||||||
private fun getSuSFS(): String {
|
private fun getSuSFS(): String {
|
||||||
return try {
|
return try {
|
||||||
getSuSFSVersion()
|
getSuSFSVersion()
|
||||||
@@ -86,6 +95,74 @@ object SuSFSManager {
|
|||||||
return context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
|
return context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行命令的通用方法
|
||||||
|
*/
|
||||||
|
private fun runCmd(shell: Shell, cmd: String): String {
|
||||||
|
return shell.newJob()
|
||||||
|
.add(cmd)
|
||||||
|
.to(mutableListOf<String>(), null)
|
||||||
|
.exec().out
|
||||||
|
.joinToString("\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前槽位信息
|
||||||
|
*/
|
||||||
|
suspend fun getCurrentSlotInfo(): List<SlotInfo> = withContext(Dispatchers.IO) {
|
||||||
|
try {
|
||||||
|
val shell = getRootShell()
|
||||||
|
val slotInfoList = mutableListOf<SlotInfo>()
|
||||||
|
|
||||||
|
// 获取boot_a槽位信息
|
||||||
|
val bootAUname = runCmd(shell,
|
||||||
|
"strings -n 20 /dev/block/by-name/boot_a | awk '/Linux version/ && ++c==2 {print $3; exit}'"
|
||||||
|
).trim()
|
||||||
|
val bootABuildTime = runCmd(shell, "strings -n 20 /dev/block/by-name/boot_a | sed -n '/Linux version.*#/{s/.*#/#/p;q}'").trim()
|
||||||
|
|
||||||
|
if (bootAUname.isNotEmpty() && bootABuildTime.isNotEmpty()) {
|
||||||
|
val uname = bootAUname.ifEmpty { "unknown" }
|
||||||
|
val buildTime = bootABuildTime.ifEmpty { "unknown" }
|
||||||
|
slotInfoList.add(SlotInfo("boot_a", uname, buildTime))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取boot_b槽位信息
|
||||||
|
val bootBUname = runCmd(shell,
|
||||||
|
"strings -n 20 /dev/block/by-name/boot_b | awk '/Linux version/ && ++c==2 {print $3; exit}'"
|
||||||
|
).trim()
|
||||||
|
val bootBBuildTime = runCmd(shell, "strings -n 20 /dev/block/by-name/boot_b | sed -n '/Linux version.*#/{s/.*#/#/p;q}'").trim()
|
||||||
|
|
||||||
|
if (bootBUname.isNotEmpty() && bootBBuildTime.isNotEmpty()) {
|
||||||
|
val uname = bootBUname.ifEmpty { "unknown" }
|
||||||
|
val buildTime = bootBBuildTime.ifEmpty { "unknown" }
|
||||||
|
slotInfoList.add(SlotInfo("boot_b", uname, buildTime))
|
||||||
|
}
|
||||||
|
|
||||||
|
slotInfoList
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
emptyList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前活动槽位
|
||||||
|
*/
|
||||||
|
suspend fun getCurrentActiveSlot(): String = withContext(Dispatchers.IO) {
|
||||||
|
try {
|
||||||
|
val shell = getRootShell()
|
||||||
|
val suffix = runCmd(shell, "getprop ro.boot.slot_suffix").trim()
|
||||||
|
when (suffix) {
|
||||||
|
"_a" -> "boot_a"
|
||||||
|
"_b" -> "boot_b"
|
||||||
|
else -> "unknown"
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
"unknown"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 保存uname值
|
* 保存uname值
|
||||||
*/
|
*/
|
||||||
@@ -884,7 +961,7 @@ object SuSFSManager {
|
|||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
Toast.makeText(
|
Toast.makeText(
|
||||||
context,
|
context,
|
||||||
"SuSFS self-startup module is enabled, module path:$MODULE_PATH",
|
context.getString(R.string.susfs_autostart_enabled_success, MODULE_PATH),
|
||||||
Toast.LENGTH_LONG
|
Toast.LENGTH_LONG
|
||||||
).show()
|
).show()
|
||||||
}
|
}
|
||||||
@@ -906,7 +983,7 @@ object SuSFSManager {
|
|||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
Toast.makeText(
|
Toast.makeText(
|
||||||
context,
|
context,
|
||||||
"SuSFS自启动模块已禁用",
|
context.getString(R.string.susfs_autostart_disabled_success),
|
||||||
Toast.LENGTH_SHORT
|
Toast.LENGTH_SHORT
|
||||||
).show()
|
).show()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -89,7 +89,6 @@ import androidx.compose.runtime.saveable.rememberSaveable
|
|||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
||||||
import com.sukisu.ultra.ksuApp
|
import com.sukisu.ultra.ksuApp
|
||||||
import com.ramcosta.composedestinations.generated.destinations.SuSFSConfigScreenDestination
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author ShirkNeko
|
* @author ShirkNeko
|
||||||
@@ -1158,21 +1157,6 @@ fun MoreSettingsScreen(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// SuSFS 配置(仅在支持时显示)
|
|
||||||
if (getSuSFS() == "Supported" && SuSFSManager.isBinaryAvailable(context)) {
|
|
||||||
SettingItem(
|
|
||||||
icon = Icons.Default.Settings,
|
|
||||||
title = stringResource(R.string.susfs_config_setting_title),
|
|
||||||
subtitle = stringResource(
|
|
||||||
R.string.susfs_config_setting_summary,
|
|
||||||
SuSFSManager.getUnameValue(context)
|
|
||||||
),
|
|
||||||
onClick = {
|
|
||||||
navigator.navigate(SuSFSConfigScreenDestination)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SuSFS 开关(仅在支持时显示)
|
// SuSFS 开关(仅在支持时显示)
|
||||||
val suSFS = getSuSFS()
|
val suSFS = getSuSFS()
|
||||||
val isSUS_SU = getSuSFSFeatures()
|
val isSUS_SU = getSuSFSFeatures()
|
||||||
|
|||||||
@@ -529,4 +529,18 @@
|
|||||||
<string name="susfs_execution_location_post_fs_data">Post-FS-Data</string>
|
<string name="susfs_execution_location_post_fs_data">Post-FS-Data</string>
|
||||||
<string name="susfs_execution_location_service_description">在系统服务启动后执行</string>
|
<string name="susfs_execution_location_service_description">在系统服务启动后执行</string>
|
||||||
<string name="susfs_execution_location_post_fs_data_description">在文件系统挂载后但系统完全启动前执行,可能会导致循环重启</string>
|
<string name="susfs_execution_location_post_fs_data_description">在文件系统挂载后但系统完全启动前执行,可能会导致循环重启</string>
|
||||||
|
<string name="susfs_slot_info_title">槽位信息</string>
|
||||||
|
<string name="susfs_slot_info_description">查看当前启动槽位信息并复制数值</string>
|
||||||
|
<string name="susfs_current_active_slot">当前活动槽位:%s</string>
|
||||||
|
<string name="susfs_slot_name">槽位:%s</string>
|
||||||
|
<string name="susfs_slot_uname">Uname:%s</string>
|
||||||
|
<string name="susfs_slot_build_time">构建时间:%s</string>
|
||||||
|
<string name="susfs_slot_current_badge">当前</string>
|
||||||
|
<string name="susfs_slot_use_uname">使用Uname</string>
|
||||||
|
<string name="susfs_slot_use_build_time">使用构建时间</string>
|
||||||
|
<string name="susfs_slot_info_unavailable">无法获取槽位信息</string>
|
||||||
|
<string name="susfs_slot_info_loading">正在加载槽位信息…</string>
|
||||||
|
<!-- SuSFS 自启动相关字符串 -->
|
||||||
|
<string name="susfs_autostart_enabled_success">SuSFS自启动模块已启用,模块路径:%s</string>
|
||||||
|
<string name="susfs_autostart_disabled_success">SuSFS自启动模块已禁用</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -531,4 +531,18 @@
|
|||||||
<string name="susfs_execution_location_post_fs_data">Post-FS-Data</string>
|
<string name="susfs_execution_location_post_fs_data">Post-FS-Data</string>
|
||||||
<string name="susfs_execution_location_service_description">Execute after system services start</string>
|
<string name="susfs_execution_location_service_description">Execute after system services start</string>
|
||||||
<string name="susfs_execution_location_post_fs_data_description">Execute after file system is mounted but before system is fully booted,May cause a boot loop</string>
|
<string name="susfs_execution_location_post_fs_data_description">Execute after file system is mounted but before system is fully booted,May cause a boot loop</string>
|
||||||
|
<string name="susfs_slot_info_title">Slot Information</string>
|
||||||
|
<string name="susfs_slot_info_description">View current boot slot information and copy values</string>
|
||||||
|
<string name="susfs_current_active_slot">Current Active Slot: %s</string>
|
||||||
|
<string name="susfs_slot_name">Slot: %s</string>
|
||||||
|
<string name="susfs_slot_uname">Uname: %s</string>
|
||||||
|
<string name="susfs_slot_build_time">Build Time: %s</string>
|
||||||
|
<string name="susfs_slot_current_badge">Current</string>
|
||||||
|
<string name="susfs_slot_use_uname">Use Uname</string>
|
||||||
|
<string name="susfs_slot_use_build_time">Use Build Time</string>
|
||||||
|
<string name="susfs_slot_info_unavailable">Unable to retrieve slot information</string>
|
||||||
|
<string name="susfs_slot_info_loading">Loading slot information…</string>
|
||||||
|
<!-- SuSFS 自启动相关字符串 -->
|
||||||
|
<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>
|
||||||
</resources>
|
</resources>
|
||||||
Reference in New Issue
Block a user