This commit is contained in:
liankong
2025-04-11 15:19:48 +08:00
11 changed files with 968 additions and 193 deletions

View File

@@ -9,7 +9,7 @@ import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.annotation.StringRes import androidx.annotation.StringRes
import androidx.compose.foundation.LocalIndication import androidx.compose.foundation.LocalIndication
import androidx.compose.foundation.background import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
@@ -29,11 +29,7 @@ import androidx.compose.ui.semantics.Role
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.documentfile.provider.DocumentFile import androidx.documentfile.provider.DocumentFile
import com.maxkeppeker.sheets.core.models.base.Header
import com.maxkeppeker.sheets.core.models.base.rememberUseCaseState
import com.maxkeppeler.sheets.list.ListDialog
import com.maxkeppeler.sheets.list.models.ListOption import com.maxkeppeler.sheets.list.models.ListOption
import com.maxkeppeler.sheets.list.models.ListSelection
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.FlashScreenDestination import com.ramcosta.composedestinations.generated.destinations.FlashScreenDestination
@@ -43,6 +39,9 @@ import shirkneko.zako.sukisu.R
import shirkneko.zako.sukisu.ui.component.DialogHandle import shirkneko.zako.sukisu.ui.component.DialogHandle
import shirkneko.zako.sukisu.ui.component.rememberConfirmDialog import shirkneko.zako.sukisu.ui.component.rememberConfirmDialog
import shirkneko.zako.sukisu.ui.component.rememberCustomDialog import shirkneko.zako.sukisu.ui.component.rememberCustomDialog
import shirkneko.zako.sukisu.ui.theme.ThemeConfig
import shirkneko.zako.sukisu.ui.theme.getCardColors
import shirkneko.zako.sukisu.ui.theme.getCardElevation
import shirkneko.zako.sukisu.ui.util.* import shirkneko.zako.sukisu.ui.util.*
import shirkneko.zako.sukisu.utils.AssetsUtil import shirkneko.zako.sukisu.utils.AssetsUtil
import java.io.File import java.io.File
@@ -500,35 +499,79 @@ fun rememberSelectKmiDialog(onSelected: (String?) -> Unit): DialogHandle {
val supportedKmi by produceState(initialValue = emptyList<String>()) { val supportedKmi by produceState(initialValue = emptyList<String>()) {
value = getSupportedKmis() value = getSupportedKmis()
} }
val options = supportedKmi.map { value -> val listOptions = supportedKmi.map { value ->
ListOption( ListOption(
titleText = value titleText = value,
subtitleText = null,
icon = null
) )
} }
var selection by remember { mutableStateOf<String?>(null) } var selection: String? = null
Box( val cardColor = if (!ThemeConfig.useDynamicColor) {
modifier = Modifier.background(MaterialTheme.colorScheme.surface) ThemeConfig.currentTheme.ButtonContrast
) { } else {
ListDialog( MaterialTheme.colorScheme.secondaryContainer
state = rememberUseCaseState(visible = true, onFinishedRequest = {
onSelected(selection)
}, onCloseRequest = {
dismiss()
}),
header = Header.Default(
title = stringResource(R.string.select_kmi),
),
selection = ListSelection.Single(
showRadioButtons = true,
options = options,
) { _, option ->
selection = option.titleText
} }
AlertDialog(
onDismissRequest = {
dismiss()
},
title = {
Text(text = stringResource(R.string.select_kmi))
},
text = {
Column {
listOptions.forEachIndexed { index, option ->
Row(
modifier = Modifier
.clickable {
selection = supportedKmi[index]
}
.padding(vertical = 8.dp)
) {
Column {
Text(text = option.titleText)
option.subtitleText?.let {
Text(
text = it,
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant
) )
} }
} }
} }
}
}
},
confirmButton = {
TextButton(
onClick = {
if (selection != null) {
onSelected(selection)
}
dismiss()
}
) {
Text(text = stringResource(android.R.string.ok))
}
},
dismissButton = {
TextButton(
onClick = {
dismiss()
}
) {
Text(text = stringResource(android.R.string.cancel))
}
},
containerColor = getCardColors(cardColor.copy(alpha = 0.9f)).containerColor.copy(alpha = 0.9f),
shape = MaterialTheme.shapes.medium,
tonalElevation = getCardElevation()
)
}
}
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable

View File

@@ -20,9 +20,11 @@ import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.Undo import androidx.compose.material.icons.automirrored.filled.Undo
import androidx.compose.material.icons.filled.* import androidx.compose.material.icons.filled.*
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.ListItem import androidx.compose.material3.ListItem
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ModalBottomSheet import androidx.compose.material3.ModalBottomSheet
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.material3.SnackbarHost import androidx.compose.material3.SnackbarHost
@@ -49,12 +51,9 @@ import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.core.content.FileProvider import androidx.core.content.FileProvider
import com.maxkeppeker.sheets.core.models.base.Header import androidx.core.content.edit
import com.maxkeppeker.sheets.core.models.base.IconSource import com.maxkeppeker.sheets.core.models.base.IconSource
import com.maxkeppeker.sheets.core.models.base.rememberUseCaseState
import com.maxkeppeler.sheets.list.ListDialog
import com.maxkeppeler.sheets.list.models.ListOption import com.maxkeppeler.sheets.list.models.ListOption
import com.maxkeppeler.sheets.list.models.ListSelection
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.AppProfileTemplateScreenDestination import com.ramcosta.composedestinations.generated.destinations.AppProfileTemplateScreenDestination
@@ -75,14 +74,14 @@ import shirkneko.zako.sukisu.ui.component.SwitchItem
import shirkneko.zako.sukisu.ui.component.rememberConfirmDialog import shirkneko.zako.sukisu.ui.component.rememberConfirmDialog
import shirkneko.zako.sukisu.ui.component.rememberCustomDialog import shirkneko.zako.sukisu.ui.component.rememberCustomDialog
import shirkneko.zako.sukisu.ui.component.rememberLoadingDialog import shirkneko.zako.sukisu.ui.component.rememberLoadingDialog
import shirkneko.zako.sukisu.ui.theme.CardConfig
import shirkneko.zako.sukisu.ui.theme.ThemeConfig
import shirkneko.zako.sukisu.ui.theme.getCardColors
import shirkneko.zako.sukisu.ui.theme.getCardElevation
import shirkneko.zako.sukisu.ui.util.LocalSnackbarHost import shirkneko.zako.sukisu.ui.util.LocalSnackbarHost
import shirkneko.zako.sukisu.ui.util.getBugreportFile import shirkneko.zako.sukisu.ui.util.getBugreportFile
import java.time.LocalDateTime import java.time.LocalDateTime
import java.time.format.DateTimeFormatter import java.time.format.DateTimeFormatter
import androidx.compose.material.icons.filled.ExpandMore
import androidx.compose.material3.MaterialTheme
import shirkneko.zako.sukisu.ui.theme.CardConfig
import androidx.core.content.edit
/** /**
@@ -111,7 +110,6 @@ fun SettingScreen(navigator: DestinationsNavigator) {
AboutDialog(it) AboutDialog(it)
} }
val loadingDialog = rememberLoadingDialog() val loadingDialog = rememberLoadingDialog()
val shrinkDialog = rememberConfirmDialog()
// endregion // endregion
Column( Column(
@@ -451,20 +449,73 @@ fun rememberUninstallDialog(onSelected: (UninstallType) -> Unit): DialogHandle {
} }
var selection = UninstallType.NONE var selection = UninstallType.NONE
ListDialog(state = rememberUseCaseState(visible = true, onFinishedRequest = { val cardColor = if (!ThemeConfig.useDynamicColor) {
ThemeConfig.currentTheme.ButtonContrast
} else {
MaterialTheme.colorScheme.secondaryContainer
}
AlertDialog(
onDismissRequest = {
dismiss()
},
title = {
Text(text = stringResource(R.string.settings_uninstall))
},
text = {
Column {
listOptions.forEachIndexed { index, option ->
Row(
modifier = Modifier
.clickable {
selection = options[index]
}
.padding(vertical = 8.dp)
) {
Icon(
imageVector = options[index].icon,
contentDescription = null,
modifier = Modifier.padding(end = 8.dp)
)
Column {
Text(text = option.titleText)
option.subtitleText?.let {
Text(
text = it,
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
}
}
}
}
}
},
confirmButton = {
androidx.compose.material3.TextButton(
onClick = {
if (selection != UninstallType.NONE) { if (selection != UninstallType.NONE) {
onSelected(selection) onSelected(selection)
} }
}, onCloseRequest = {
dismiss() dismiss()
}), header = Header.Default( }
title = stringResource(R.string.settings_uninstall), ) {
), selection = ListSelection.Single( Text(text = stringResource(android.R.string.ok))
showRadioButtons = false, }
options = listOptions, },
) { index, _ -> dismissButton = {
selection = options[index] androidx.compose.material3.TextButton(
}) onClick = {
dismiss()
}
) {
Text(text = stringResource(android.R.string.cancel))
}
},
containerColor = getCardColors(cardColor.copy(alpha = 0.9f)).containerColor.copy(alpha = 0.9f),
shape = MaterialTheme.shapes.medium,
tonalElevation = getCardElevation()
)
} }
} }

View File

@@ -29,7 +29,6 @@ import shirkneko.zako.sukisu.R
import shirkneko.zako.sukisu.ui.component.ConfirmResult import shirkneko.zako.sukisu.ui.component.ConfirmResult
import shirkneko.zako.sukisu.ui.component.SearchAppBar import shirkneko.zako.sukisu.ui.component.SearchAppBar
import shirkneko.zako.sukisu.ui.component.rememberConfirmDialog import shirkneko.zako.sukisu.ui.component.rememberConfirmDialog
import shirkneko.zako.sukisu.ui.component.rememberLoadingDialog
import shirkneko.zako.sukisu.ui.theme.getCardColors import shirkneko.zako.sukisu.ui.theme.getCardColors
import shirkneko.zako.sukisu.ui.theme.getCardElevation import shirkneko.zako.sukisu.ui.theme.getCardElevation
import shirkneko.zako.sukisu.ui.viewmodel.KpmViewModel import shirkneko.zako.sukisu.ui.viewmodel.KpmViewModel
@@ -38,6 +37,10 @@ import shirkneko.zako.sukisu.ui.util.unloadKpmModule
import java.io.File import java.io.File
import androidx.core.content.edit import androidx.core.content.edit
import shirkneko.zako.sukisu.ui.theme.ThemeConfig import shirkneko.zako.sukisu.ui.theme.ThemeConfig
import shirkneko.zako.sukisu.ui.component.rememberCustomDialog
import shirkneko.zako.sukisu.ui.component.ConfirmDialogHandle
import java.net.URLDecoder
import java.net.URLEncoder
/** /**
* KPM 管理界面 * KPM 管理界面
@@ -55,7 +58,6 @@ fun KpmScreen(
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
val snackBarHost = remember { SnackbarHostState() } val snackBarHost = remember { SnackbarHostState() }
val confirmDialog = rememberConfirmDialog() val confirmDialog = rememberConfirmDialog()
val loadingDialog = rememberLoadingDialog()
val cardColor = if (!ThemeConfig.useDynamicColor) { val cardColor = if (!ThemeConfig.useDynamicColor) {
ThemeConfig.currentTheme.ButtonContrast ThemeConfig.currentTheme.ButtonContrast
} else { } else {
@@ -64,17 +66,86 @@ fun KpmScreen(
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState()) val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
val kpmInstall = stringResource(R.string.kpm_install)
val kpmInstallConfirm = stringResource(R.string.kpm_install_confirm)
val kpmInstallSuccess = stringResource(R.string.kpm_install_success) val kpmInstallSuccess = stringResource(R.string.kpm_install_success)
val kpmInstallFailed = stringResource(R.string.kpm_install_failed) val kpmInstallFailed = stringResource(R.string.kpm_install_failed)
val install = stringResource(R.string.install)
val cancel = stringResource(R.string.cancel) val cancel = stringResource(R.string.cancel)
val kpmUninstall = stringResource(R.string.kpm_uninstall)
val kpmUninstallConfirmTemplate = stringResource(R.string.kpm_uninstall_confirm)
val uninstall = stringResource(R.string.uninstall) val uninstall = stringResource(R.string.uninstall)
val FailedtoCheckModuleFile = stringResource(R.string.snackbar_failed_to_check_module_file)
val kpmUninstallSuccess = stringResource(R.string.kpm_uninstall_success) val kpmUninstallSuccess = stringResource(R.string.kpm_uninstall_success)
val kpmUninstallFailed = stringResource(R.string.kpm_uninstall_failed) val kpmUninstallFailed = stringResource(R.string.kpm_uninstall_failed)
val kpmInstallMode = stringResource(R.string.kpm_install_mode)
val kpmInstallModeLoad = stringResource(R.string.kpm_install_mode_load)
val kpmInstallModeEmbed = stringResource(R.string.kpm_install_mode_embed)
val kpmInstallModeDescription = stringResource(R.string.kpm_install_mode_description)
var tempFileForInstall by remember { mutableStateOf<File?>(null) }
val installModeDialog = rememberCustomDialog { dismiss ->
AlertDialog(
onDismissRequest = {
dismiss()
tempFileForInstall?.delete()
tempFileForInstall = null
},
title = { Text(kpmInstallMode) },
text = { Text(kpmInstallModeDescription) },
confirmButton = {
Column {
Button(
onClick = {
scope.launch {
dismiss()
tempFileForInstall?.let { tempFile ->
handleModuleInstall(
tempFile = tempFile,
isEmbed = false,
viewModel = viewModel,
snackBarHost = snackBarHost,
kpmInstallSuccess = kpmInstallSuccess,
kpmInstallFailed = kpmInstallFailed
)
}
tempFileForInstall = null
}
}
) {
Text(kpmInstallModeLoad)
}
Spacer(modifier = Modifier.height(8.dp))
Button(
onClick = {
scope.launch {
dismiss()
tempFileForInstall?.let { tempFile ->
handleModuleInstall(
tempFile = tempFile,
isEmbed = true,
viewModel = viewModel,
snackBarHost = snackBarHost,
kpmInstallSuccess = kpmInstallSuccess,
kpmInstallFailed = kpmInstallFailed
)
}
tempFileForInstall = null
}
}
) {
Text(kpmInstallModeEmbed)
}
}
},
dismissButton = {
TextButton(
onClick = {
dismiss()
tempFileForInstall?.delete()
tempFileForInstall = null
}
) {
Text(cancel)
}
}
)
}
val selectPatchLauncher = rememberLauncherForActivityResult( val selectPatchLauncher = rememberLauncherForActivityResult(
contract = ActivityResultContracts.StartActivityForResult() contract = ActivityResultContracts.StartActivityForResult()
@@ -84,8 +155,10 @@ fun KpmScreen(
val uri = result.data?.data ?: return@rememberLauncherForActivityResult val uri = result.data?.data ?: return@rememberLauncherForActivityResult
scope.launch { scope.launch {
// 复制文件到临时目录 val fileName = uri.lastPathSegment ?: "unknown.kpm"
val tempFile = File(context.cacheDir, "temp_patch.kpm") val encodedFileName = URLEncoder.encode(fileName, "UTF-8")
val tempFile = File(context.cacheDir, encodedFileName)
context.contentResolver.openInputStream(uri)?.use { input -> context.contentResolver.openInputStream(uri)?.use { input ->
tempFile.outputStream().use { output -> tempFile.outputStream().use { output ->
input.copyTo(output) input.copyTo(output)
@@ -101,43 +174,8 @@ fun KpmScreen(
return@launch return@launch
} }
val confirmResult = confirmDialog.awaitConfirm( tempFileForInstall = tempFile
title = kpmInstall, installModeDialog.show()
content = kpmInstallConfirm,
confirm = install,
dismiss = cancel
)
if (confirmResult == ConfirmResult.Confirmed) {
val success = loadingDialog.withLoading {
try {
val loadResult = loadKpmModule(tempFile.absolutePath)
if (true && loadResult.startsWith("Error")) {
Log.e("KsuCli", "Failed to load KPM module: $loadResult")
false
} else {
true
}
} catch (e: Exception) {
Log.e("KsuCli", "Failed to load KPM module: ${e.message}", e)
false
}
}
if (success) {
viewModel.fetchModuleList()
snackBarHost.showSnackbar(
message = kpmInstallSuccess,
duration = SnackbarDuration.Short
)
} else {
snackBarHost.showSnackbar(
message = kpmInstallFailed,
duration = SnackbarDuration.Short
)
}
}
tempFile.delete()
} }
} }
@@ -234,46 +272,21 @@ fun KpmScreen(
verticalArrangement = Arrangement.spacedBy(16.dp) verticalArrangement = Arrangement.spacedBy(16.dp)
) { ) {
items(viewModel.moduleList) { module -> items(viewModel.moduleList) { module ->
val kpmUninstallConfirm = String.format(kpmUninstallConfirmTemplate, module.name)
KpmModuleItem( KpmModuleItem(
module = module, module = module,
onUninstall = { onUninstall = {
scope.launch { scope.launch {
val confirmResult = confirmDialog.awaitConfirm( handleModuleUninstall(
title = kpmUninstall, module = module,
content = kpmUninstallConfirm, viewModel = viewModel,
confirm = uninstall, snackBarHost = snackBarHost,
dismiss = cancel kpmUninstallSuccess = kpmUninstallSuccess,
kpmUninstallFailed = kpmUninstallFailed,
FailedtoCheckModuleFile = FailedtoCheckModuleFile,
uninstall = uninstall,
cancel = cancel,
confirmDialog = confirmDialog
) )
if (confirmResult == ConfirmResult.Confirmed) {
val success = loadingDialog.withLoading {
try {
val unloadResult = unloadKpmModule(module.id)
if (true && unloadResult.startsWith("Error")) {
Log.e("KsuCli", "Failed to unload KPM module: $unloadResult")
false
} else {
true
}
} catch (e: Exception) {
Log.e("KsuCli", "Failed to unload KPM module: ${e.message}", e)
false
}
}
if (success) {
viewModel.fetchModuleList()
snackBarHost.showSnackbar(
message = kpmUninstallSuccess,
duration = SnackbarDuration.Short
)
} else {
snackBarHost.showSnackbar(
message = kpmUninstallFailed,
duration = SnackbarDuration.Short
)
}
}
} }
}, },
onControl = { onControl = {
@@ -287,6 +300,133 @@ fun KpmScreen(
} }
} }
private suspend fun handleModuleInstall(
tempFile: File,
isEmbed: Boolean,
viewModel: KpmViewModel,
snackBarHost: SnackbarHostState,
kpmInstallSuccess: String,
kpmInstallFailed: String
) {
val moduleId = extractModuleId(tempFile.name)
if (moduleId == null) {
Log.e("KsuCli", "Failed to extract module ID from file: ${tempFile.name}")
snackBarHost.showSnackbar(
message = kpmInstallFailed,
duration = SnackbarDuration.Short
)
tempFile.delete()
return
}
val targetPath = "/data/adb/kpm/$moduleId.kpm"
try {
if (isEmbed) {
Runtime.getRuntime().exec(arrayOf("su", "-c", "mkdir -p /data/adb/kpm")).waitFor()
Runtime.getRuntime().exec(arrayOf("su", "-c", "cp ${tempFile.absolutePath} $targetPath")).waitFor()
}
val loadResult = loadKpmModule(tempFile.absolutePath)
if (loadResult.startsWith("Error")) {
Log.e("KsuCli", "Failed to load KPM module: $loadResult")
snackBarHost.showSnackbar(
message = kpmInstallFailed,
duration = SnackbarDuration.Short
)
} else {
viewModel.fetchModuleList()
snackBarHost.showSnackbar(
message = kpmInstallSuccess,
duration = SnackbarDuration.Short
)
}
} catch (e: Exception) {
Log.e("KsuCli", "Failed to load KPM module: ${e.message}", e)
snackBarHost.showSnackbar(
message = kpmInstallFailed,
duration = SnackbarDuration.Short
)
}
tempFile.delete()
}
private fun extractModuleId(fileName: String): String? {
return try {
val decodedFileName = URLDecoder.decode(fileName, "UTF-8")
val pattern = "([^/]*?)\\.kpm$".toRegex()
val matchResult = pattern.find(decodedFileName)
matchResult?.groupValues?.get(1)
} catch (e: Exception) {
Log.e("KsuCli", "Failed to extract module ID: ${e.message}", e)
null
}
}
private suspend fun handleModuleUninstall(
module: KpmViewModel.ModuleInfo,
viewModel: KpmViewModel,
snackBarHost: SnackbarHostState,
kpmUninstallSuccess: String,
kpmUninstallFailed: String,
FailedtoCheckModuleFile: String,
uninstall: String,
cancel: String,
confirmDialog: ConfirmDialogHandle
) {
val moduleFileName = "${module.id}.kpm"
val moduleFilePath = "/data/adb/kpm/$moduleFileName"
val fileExists = try {
val result = Runtime.getRuntime().exec(arrayOf("su", "-c", "ls /data/adb/kpm/$moduleFileName")).waitFor() == 0
result
} catch (e: Exception) {
Log.e("KsuCli", "Failed to check module file existence: ${e.message}", e)
snackBarHost.showSnackbar(
message = FailedtoCheckModuleFile,
duration = SnackbarDuration.Short
)
false
}
val confirmResult = confirmDialog.awaitConfirm(
title = "将卸载以下kpm模块\n$moduleFileName",
content = "The following kpm modules will be uninstalled\n$moduleFileName",
confirm = uninstall,
dismiss = cancel
)
if (confirmResult == ConfirmResult.Confirmed) {
try {
val unloadResult = unloadKpmModule(module.id)
if (unloadResult.startsWith("Error")) {
Log.e("KsuCli", "Failed to unload KPM module: $unloadResult")
snackBarHost.showSnackbar(
message = kpmUninstallFailed,
duration = SnackbarDuration.Short
)
return
}
if (fileExists) {
Runtime.getRuntime().exec(arrayOf("su", "-c", "rm $moduleFilePath")).waitFor()
}
viewModel.fetchModuleList()
snackBarHost.showSnackbar(
message = kpmUninstallSuccess,
duration = SnackbarDuration.Short
)
} catch (e: Exception) {
Log.e("KsuCli", "Failed to unload KPM module: ${e.message}", e)
snackBarHost.showSnackbar(
message = kpmUninstallFailed,
duration = SnackbarDuration.Short
)
}
}
}
@Composable @Composable
private fun KpmModuleItem( private fun KpmModuleItem(
module: KpmViewModel.ModuleInfo, module: KpmViewModel.ModuleInfo,

View File

@@ -8,26 +8,32 @@
<string name="home_superuser_count">スーパーユーザー: %d</string> <string name="home_superuser_count">スーパーユーザー: %d</string>
<string name="home_module_count">モジュール: %d</string> <string name="home_module_count">モジュール: %d</string>
<string name="home_unsupported">非対応</string> <string name="home_unsupported">非対応</string>
<string name="home_unsupported_reason">現在、 KernelSU は GKI カーネルにのみ対応しています</string> <string name="home_unsupported_reason">カーネルの KernelSU ドライバが未検出です。カーネルが間違ってませんか?</string>
<string name="home_kernel">カーネル</string> <string name="home_kernel">カーネル</string>
<string name="home_manager_version">アプリのバージョン</string> <string name="home_susfs">SuSFS: %s</string>
<string name="home_susfs_version">SuSFS のバージョン</string>
<string name="home_susfs_sus_su">SuS SU</string>
<string name="home_manager_version">マネージャーのバージョン</string>
<string name="home_fingerprint">Fingerprint</string> <string name="home_fingerprint">Fingerprint</string>
<string name="home_selinux_status">SELinux の状態</string> <string name="home_selinux_status">SELinux のステータス</string>
<string name="selinux_status_disabled">Disabled</string> <string name="selinux_status_disabled">無効</string>
<string name="selinux_status_enforcing">Enforcing</string> <string name="selinux_status_enforcing">Enforcing</string>
<string name="selinux_status_permissive">Permissive</string> <string name="selinux_status_permissive">Permissive</string>
<string name="selinux_status_unknown">不明</string> <string name="selinux_status_unknown">不明</string>
<string name="superuser">スーパーユーザー</string> <string name="superuser">スーパーユーザー</string>
<string name="module_failed_to_enable">%s モジュールをオンにできませんでした</string> <string name="module_failed_to_enable">%s モジュールを ON にできませんでした</string>
<string name="module_failed_to_disable">%s モジュールをオフにできませんでした</string> <string name="module_failed_to_disable">%s モジュールを OFF にできませんでした</string>
<string name="module_empty">モジュールがインストールされていません</string> <string name="module_empty">モジュールがインストールされていません</string>
<string name="module">モジュール</string> <string name="module">モジュール</string>
<string name="module_sort_action_first">並べ替え (アクション優先)</string>
<string name="module_sort_enabled_first">並べ替え (最初に有効)</string>
<string name="uninstall">アンインストール</string> <string name="uninstall">アンインストール</string>
<string name="restore">復元</string>
<string name="module_install">インストール</string> <string name="module_install">インストール</string>
<string name="install">インストール</string> <string name="install">インストール</string>
<string name="reboot">再起動</string> <string name="reboot">再起動</string>
<string name="settings">設定</string> <string name="settings">設定</string>
<string name="reboot_userspace">通常の再起動</string> <string name="reboot_userspace">ソフトリブート</string>
<string name="reboot_recovery">リカバリーへ再起動</string> <string name="reboot_recovery">リカバリーへ再起動</string>
<string name="reboot_bootloader">ブートローダーへ再起動</string> <string name="reboot_bootloader">ブートローダーへ再起動</string>
<string name="reboot_download">ダウンロードモードへ再起動</string> <string name="reboot_download">ダウンロードモードへ再起動</string>
@@ -37,21 +43,21 @@
<string name="module_uninstall_success">%s はアンインストールされました</string> <string name="module_uninstall_success">%s はアンインストールされました</string>
<string name="module_uninstall_failed">%s をアンインストールできませんでした</string> <string name="module_uninstall_failed">%s をアンインストールできませんでした</string>
<string name="module_version">バージョン</string> <string name="module_version">バージョン</string>
<string name="module_author">作者</string> <string name="module_author">作者</string>
<string name="refresh">更新</string> <string name="refresh">更新</string>
<string name="show_system_apps">システムアプリを表示</string> <string name="show_system_apps">システムアプリを表示</string>
<string name="hide_system_apps">システムアプリを非表示</string> <string name="hide_system_apps">システムアプリを非表示</string>
<string name="send_log">ログを送信</string> <string name="send_log">ログを送信</string>
<string name="safe_mode">セーフモード</string> <string name="safe_mode">セーフモード</string>
<string name="reboot_to_apply">再起動すると有効化されます</string> <string name="reboot_to_apply">再起動すると有効化されます</string>
<string name="module_magisk_conflict">モジュールが Magisk との競合により利用できません!</string> <string name="module_magisk_conflict">モジュールが Magisk との競合により利用できません</string>
<string name="home_learn_kernelsu">KernelSU について</string> <string name="home_learn_kernelsu">KernelSU について学ぶ</string>
<string name="home_learn_kernelsu_url">https://kernelsu.org/ja_JP/guide/what-is-kernelsu.html</string> <string name="home_learn_kernelsu_url">https://kernelsu.org/ja_JP/guide/what-is-kernelsu.html</string>
<string name="home_click_to_learn_kernelsu">KernelSU のインストール方法やモジュールの使い方はこちら</string> <string name="home_click_to_learn_kernelsu">KernelSU のインストール方法やモジュールの使い方を学習できます</string>
<string name="home_support_title">支援する</string> <string name="home_support_title">支援する</string>
<string name="home_support_content">KernelSU はこれからもずっと無料でオープンソースです。寄付をして頂くことで、開発を支援していただけます。</string> <string name="home_support_content">KernelSU は今後も無料でオープンソースです。ですが、寄付をして頂けると開発者への貢献になります。</string>
<string name="profile">アプリのプロファイル</string> <string name="about_source_code"><![CDATA[ソースコードは %1$s で確認できます。<br/>%2$s チャンネルにご参加ください。]]></string>
<string name="profile_default">既定</string> <string name="profile_default">デフォルト</string>
<string name="profile_template">テンプレート</string> <string name="profile_template">テンプレート</string>
<string name="profile_custom">カスタム</string> <string name="profile_custom">カスタム</string>
<string name="profile_name">プロファイル名</string> <string name="profile_name">プロファイル名</string>
@@ -59,76 +65,201 @@
<string name="profile_namespace_inherited">継承</string> <string name="profile_namespace_inherited">継承</string>
<string name="profile_namespace_global">共通</string> <string name="profile_namespace_global">共通</string>
<string name="profile_namespace_individual">分離</string> <string name="profile_namespace_individual">分離</string>
<string name="profile_umount_modules">モジュールのアンマウント</string>
<string name="profile_groups">グループ</string> <string name="profile_groups">グループ</string>
<string name="profile_capabilities">ケーパビリティ</string>
<string name="profile_selinux_context">SELinux コンテキスト</string> <string name="profile_selinux_context">SELinux コンテキスト</string>
<string name="profile_umount_modules">モジュールのアンマウント</string>
<string name="failed_to_update_app_profile">%s のアプリのプロファイルの更新をできませでした</string> <string name="failed_to_update_app_profile">%s のアプリのプロファイルの更新をできませでした</string>
<string name="require_kernel_version" formatted="false">現在の KernelSU のバージョン %d は低すぎるため、マネージャーは正常に動作しません。バージョン %d 以上に更新してください!</string>
<string name="settings_umount_modules_default">デフォルトでモジュールのマウントを解除</string>
<string name="settings_umount_modules_default_summary">アプリプロファイルの「モジュールのアンマウント」の共通のデフォルト値です。 有効にすると、プロファイルセットを持たないアプリのシステムに対するすべてのモジュールの変更が削除されます。</string>
<string name="settings_susfs_toggle">Kprobe フックを非表示にする</string>
<string name="settings_susfs_toggle_summary">KSU によって生成された Kprobe フックを無効化して、代替となる組み込みの非 Kprobe を有効化します。Kprobe をサポートしない 非 GKI カーネルに適用される同等の機能を実装します。</string>
<string name="profile_umount_modules_summary">このオプションを有効にすると、KernelSU はこのアプリのモジュールによって変更されたファイルを復元できるようになります。</string>
<string name="profile_selinux_domain">ドメイン</string> <string name="profile_selinux_domain">ドメイン</string>
<string name="profile_selinux_rules">ルール</string> <string name="profile_selinux_rules">ルール</string>
<string name="new_version_available">新しいバージョン %s が利用可能です。タップしてダウンロード。</string> <string name="module_update">更新</string>
<string name="module_update">アップデート</string> <string name="module_downloading">モジュールをダウンロード中: %s</string>
<string name="module_start_downloading">ダウンロードを開始: %s</string> <string name="module_start_downloading">ダウンロードを開始: %s</string>
<string name="new_version_available">新しいバージョン %s が利用可能です。タップしてダウンロード。</string>
<string name="launch_app">起動</string> <string name="launch_app">起動</string>
<string name="force_stop_app" formatted="false">強制停止</string> <string name="force_stop_app" formatted="false">強制停止</string>
<string name="restart_app">再起動</string> <string name="restart_app">再起動</string>
<string name="failed_to_update_sepolicy">SELinux ルールの更新に失敗しました %s</string> <string name="failed_to_update_sepolicy">SELinux ルールの更新に失敗しました %s</string>
<string name="profile_capabilities">ケーパビリティ</string>
<string name="module_downloading">モジュールをダウンロード中: %s</string>
<string name="profile_umount_modules_summary">このオプションを有効にすると、KernelSU はこのアプリのモジュールによって変更されたファイルを復元できるようになります。</string>
<string name="settings_umount_modules_default">既定でモジュールのマウントを解除</string>
<string name="settings_umount_modules_default_summary">アプリプロファイルの「モジュールのアンマウント」の共通のデフォルト値です。 有効にすると、プロファイルセットを持たないアプリのシステムに対するすべてのモジュールの変更が削除されます。</string>
<string name="module_changelog">変更履歴</string> <string name="module_changelog">変更履歴</string>
<string name="app_profile_template_import_success">インポート成功</string> <string name="settings_profile_template">アプリプロファイルのテンプレート</string>
<string name="app_profile_export_to_clipboard">クリップボードからエクスポート</string> <string name="settings_profile_template_summary">アプリプロファイルのローカルおよびオンラインテンプレートを管理する</string>
<string name="app_profile_template_export_empty">エクスポートするローカル テンプレートが見つかりません!</string>
<string name="app_profile_template_id_exist">テンプレート ID はすでに存在します!</string>
<string name="app_profile_import_from_clipboard">クリップボードからインポート</string>
<string name="module_changelog_failed">変更ログの取得に失敗しました: %s</string>
<string name="app_profile_template_name">名前</string>
<string name="app_profile_template_id_invalid">無効なテンプレート ID</string>
<string name="app_profile_template_sync">オンラインテンプレートの同期</string>
<string name="app_profile_template_create">テンプレートの作成</string> <string name="app_profile_template_create">テンプレートの作成</string>
<string name="app_profile_template_readonly">読み取り専用</string>
<string name="app_profile_import_export">インポート/エクスポート</string>
<string name="app_profile_template_save_failed">テンプレートの保存に失敗しました</string>
<string name="app_profile_template_edit">テンプレートの編集</string> <string name="app_profile_template_edit">テンプレートの編集</string>
<string name="app_profile_template_id">ID</string> <string name="app_profile_template_id">ID</string>
<string name="settings_profile_template">アプリプロファイルのテンプレート</string> <string name="app_profile_template_id_invalid">無効なテンプレート ID</string>
<string name="app_profile_template_name">名前</string>
<string name="app_profile_template_description">説明</string> <string name="app_profile_template_description">説明</string>
<string name="app_profile_template_save">保存</string> <string name="app_profile_template_save">保存</string>
<string name="settings_profile_template_summary">アプリプロファイルのローカルおよびオンラインテンプレートを管理する</string>
<string name="app_profile_template_delete">消去</string> <string name="app_profile_template_delete">消去</string>
<string name="app_profile_template_import_empty">クリップボードが空です!</string>
<string name="app_profile_template_view">テンプレートを表示</string> <string name="app_profile_template_view">テンプレートを表示</string>
<string name="settings_check_update">アップデートを確認</string> <string name="app_profile_template_readonly">読み取り専用</string>
<string name="settings_check_update_summary">アプリを開いたときにアップデートを自動的に確認する</string> <string name="app_profile_template_id_exist">テンプレート ID はすでに存在します!</string>
<string name="app_profile_import_export">インポートとエクスポート</string>
<string name="app_profile_import_from_clipboard">クリップボードからインポート</string>
<string name="app_profile_export_to_clipboard">クリップボードからエクスポート</string>
<string name="app_profile_template_export_empty">エクスポートするローカル テンプレートが見つかりません!</string>
<string name="app_profile_template_import_success">インポートが成功しました</string>
<string name="app_profile_template_sync">オンラインテンプレートの同期</string>
<string name="app_profile_template_save_failed">テンプレートの保存に失敗しました</string>
<string name="app_profile_template_import_empty">クリップボードが空です!</string>
<string name="module_changelog_failed">変更ログの取得に失敗しました: %s</string>
<string name="settings_check_update">更新を確認</string>
<string name="settings_check_update_summary">アプリを開いたときに更新を自動的に確認します</string>
<string name="grant_root_failed">root の付与に失敗しました!</string> <string name="grant_root_failed">root の付与に失敗しました!</string>
<string name="action">アクション</string>
<string name="open">開く</string> <string name="open">開く</string>
<string name="enable_web_debugging">WebView デバッグを有効する</string> <string name="enable_web_debugging">WebView デバッグを有効する</string>
<string name="enable_web_debugging_summary">WebUI のデバッグに使用できます。必要な場合にのみ有効にしてください。</string> <string name="enable_web_debugging_summary">WebUI のデバッグに使用できます。必要な場合にのみ有効にしてください。</string>
<string name="select_file_tip">%1$s パーティション イメージが推奨されます</string> <string name="direct_install">直接インストール (推奨)</string>
<string name="select_kmi">KMI を選択してください</string> <string name="select_file">ファイルを選択してください</string>
<string name="install_next">次に</string>
<string name="install_inactive_slot">非アクティブなスロットにインストール (OTA 後)</string> <string name="install_inactive_slot">非アクティブなスロットにインストール (OTA 後)</string>
<string name="install_inactive_slot_warning">再起動後、デバイスは**強制的に**、現在非アクティブなスロットから起動します。 <string name="install_inactive_slot_warning">再起動後、デバイスは**強制的に**、現在非アクティブなスロットから起動します。
\nこのオプションは、OTA が完了した後にのみ使用してください。 \nこのオプションは、OTA が完了した後にのみ使用してください。
\n続</string> \n続行しますか</string>
<string name="direct_install">直接インストール (推奨)</string> <string name="install_next">次へ</string>
<string name="select_file">ファイルを選択してください</string> <string name="select_file_tip">%1$s のパーティションイメージを推奨します</string>
<string name="select_kmi">KMI を選択してください</string>
<string name="settings_uninstall">アンインストール</string>
<string name="settings_uninstall_temporary">一時的にアンインストールする</string>
<string name="settings_uninstall_permanent">完全にアンインストールする</string> <string name="settings_uninstall_permanent">完全にアンインストールする</string>
<string name="settings_restore_stock_image">ストックイメージを復元</string> <string name="settings_restore_stock_image">ストックイメージを復元</string>
<string name="settings_uninstall_temporary">一時的にアンインストールする</string>
<string name="settings_uninstall">アンインストール</string>
<string name="settings_uninstall_temporary_message">KernelSU を一時的にアンインストールし、次回の再起動後に元の状態に戻します。</string> <string name="settings_uninstall_temporary_message">KernelSU を一時的にアンインストールし、次回の再起動後に元の状態に戻します。</string>
<string name="settings_uninstall_permanent_message">KernelSU (ルートおよびすべてのモジュール) を完全かつ永久にアンインストールします。</string> <string name="settings_uninstall_permanent_message">KernelSU (root およびすべてのモジュール) を完全かつ恒久的にアンインストールします。</string>
<string name="settings_restore_stock_image_message">バックアップが存在する場合、工場出荷時のイメージを復元できます (OTA の前に使用してください)。KernelSU をアンインストールする必要がある場合は、「完全にアンインストールする」を使用してください。</string> <string name="settings_restore_stock_image_message">バックアップが存在する場合、工場出荷時のイメージを復元できます (OTA の前に使用してください)。KernelSU をアンインストールする必要がある場合は、「完全にアンインストールする」を使用してください。</string>
<string name="flashing">フラッシュ</string> <string name="flashing">フラッシュ</string>
<string name="flash_success">フラッシュ成功</string> <string name="flash_success">フラッシュ成功しました</string>
<string name="flash_failed">フラッシュ失敗</string> <string name="flash_failed">フラッシュ失敗しました</string>
<string name="selected_lkm">選択された LKM: %s</string> <string name="selected_lkm">選択された LKM: %s</string>
<string name="save_log">ログを保存</string> <string name="save_log">ログを保存</string>
<string name="action">アクション</string>
<string name="log_saved">保存されたログ</string> <string name="log_saved">保存されたログ</string>
<string name="module_sort_enabled_first">並べ替え(最初に有効)</string> <string name="status_supported">対応</string>
<string name="module_sort_action_first">並べ替え(アクション優先)</string> <string name="status_not_supported">非対応</string>
<string name="status_unknown">不明</string>
<string name="sus_su_mode">SuS SU モード:</string>
<!-- Module related -->
<string name="module_install_confirm">%1$s のモジュールをインストールしますか?</string>
<string name="unknown_module">不明なモジュール</string>
<!-- Restore related -->
<string name="restore_confirm_title">モジュールの復元を確認</string>
<string name="restore_confirm_message">この操作によりモジュールが上書きされます。続行しますか?</string>
<string name="confirm">確認</string>
<string name="cancel">キャンセル</string>
<!-- Backup related -->
<string name="backup_success">バックアップが完了しました (tar.gz)</string>
<string name="backup_failed">バックアップに失敗: %1$s</string>
<string name="backup_modules">モジュールをバックアップ</string>
<string name="restore_modules">モジュールを復元</string>
<!-- Restore related messages -->
<string name="restore_success">モジュールは正常に復元されました、再起動が必要です</string>
<string name="restore_failed">復元に失敗: %1$s</string>
<string name="restart_now">今すぐ再起動</string>
<string name="unknown_error">不明なエラー</string>
<!-- Command related -->
<string name="command_execution_failed">コマンドの実行に失敗しました: %1$s</string>
<!-- Allowlist related -->
<string name="allowlist_backup_success">許可リストのバックアップが成功しました</string>
<string name="allowlist_backup_failed">許可リストのバックアップに失敗: %1$s</string>
<string name="allowlist_restore_confirm_title">許可リストの復元を確認</string>
<string name="allowlist_restore_confirm_message">この操作により許可リストが上書きされます。続行しますか?</string>
<string name="allowlist_restore_success">許可リストの復元が成功しました</string>
<string name="allowlist_restore_failed">許可リストの復元に失敗: %1$s</string>
<string name="backup_allowlist">許可リストをバックアップ</string>
<string name="restore_allowlist">許可リストを復元</string>
<string name="settings_custom_background">カスタム背景を設定</string>
<string name="settings_custom_background_summary">カスタム背景を設定します</string>
<string name="settings_card_manage">カードの管理</string>
<string name="settings_card_alpha">カードのアルファ</string>
<string name="settings_restore_default">デフォルトに復元</string>
<string name="home_android_version">Android のバージョン</string>
<string name="home_device_model">デバイスモデル</string>
<string name="su_not_allowed">%s にスーパーユーザー権限を付与することはできません</string>
<string name="settings_disable_su">su の互換性を無効化する</string>
<string name="settings_disable_su_summary">su コマンドを使用してアプリが root 権限を取得する動作を一時的に無効化します (既存の root プロセスは影響を受けません)。</string>
<string name="using_mksu_manager">SukiSU Beta Manager を使用中です</string>
<string name="module_install_multiple_confirm">選択した %d 個のモジュールをインストールしてもよろしいですか?</string>
<string name="module_install_multiple_confirm_with_names">%1$d 個のモジュールをインストールしてもよろしいですか?\n\n%2$s</string>
<string name="more_settings">その他の設定</string>
<string name="selinux">SELinux</string>
<string name="selinux_enabled">有効</string>
<string name="selinux_disabled">無効</string>
<string name="simple_mode">シンプルモード</string>
<string name="simple_mode_summary">ON にすると不要なカードを隠します</string>
<string name="hide_kernel_kernelsu_version">カーネルのバージョンを非表示にする</string>
<string name="hide_kernel_kernelsu_version_summary">カーネルのバージョンを非表示にします</string>
<string name="hide_other_info">その他の情報を非表示にする</string>
<string name="hide_other_info_summary">ホームページ上のスーパーユーザー、モジュール、KPM モジュールの数に関する情報を隠します。</string>
<string name="hide_susfs_status">SuSFS ステータスを非表示にする</string>
<string name="hide_susfs_status_summary">ホームページ上の SuSFS ステータス情報を隠します</string>
<string name="theme_mode">テーマモード</string>
<string name="theme_follow_system">システムに従う</string>
<string name="theme_light">ライトカラー</string>
<string name="theme_dark">ダークカラー</string>
<string name="manual_hook">手動でフック</string>
<string name="dynamic_color_title">ダイナミックカラー</string>
<string name="dynamic_color_summary">システムテーマのダイナミックカラーを使用します</string>
<string name="choose_theme_color">テーマカラーを選択</string>
<string name="color_default">ホワイト</string>
<string name="color_blue">ブルー</string>
<string name="color_green">グリーン</string>
<string name="color_purple">パープル</string>
<string name="color_orange">オレンジ</string>
<string name="color_pink">ピンク</string>
<string name="color_gray">グレー</string>
<string name="color_ivory">アイボリー</string>
<string name="flash_option">ブラシの設定</string>
<string name="flash_option_tip">フラッシュするファイルを選択</string>
<string name="horizon_kernel">AnyKernel3 をフラッシュ</string>
<string name="root_required">root 権限が必要です</string>
<string name="copy_failed">ファイルのコピーに失敗しました</string>
<string name="reboot_complete_title">スクラブが完了しました</string>
<string name="reboot_complete_msg">すぐに再起動しますか?</string>
<string name="yes">はい</string>
<string name="no">いいえ</string>
<string name="failed_reboot">再起動に失敗しました</string>
<string name="batch_authorization">bulk ライセンス</string>
<string name="batch_cancel_authorization">認証を一括でキャンセル</string>
<string name="backup">バックアップ</string>
<string name="color_yellow">イエロー</string>
<string name="kpm">KPM モジュール</string>
<string name="kpm_title">カーネルモジュール</string>
<string name="kpm_empty">カーネルモジュールは現在インストールされていません</string>
<string name="kpm_version">リリース</string>
<string name="kpm_author">作者</string>
<string name="kpm_uninstall">アンインストール</string>
<string name="kpm_uninstall_success">アンインストールに失敗しました</string>
<string name="kpm_uninstall_failed">アンインストールに失敗しました</string>
<string name="kpm_install">インストールを選択</string>
<string name="kpm_install_success">KPM モジュールの読み込みに成功しました</string>
<string name="kpm_install_failed">KPM モジュールの読み込みに失敗しました</string>
<string name="kpm_args">KPM パラメーター</string>
<string name="kpm_control">完全</string>
<string name="home_kpm_version">KPM のバージョン</string>
<string name="close_notice">閉じる</string>
<string name="kernel_module_notice">以下のカーネルモジュール関数は KernelPatch によって開発され、SukiSU Ultra のカーネルモジュール関数を含むように変更されました</string>
<string name="home_ContributionCard_kernelsu">SukiSU Ultra の今後にご期待ください</string>
<string name="kpm_control_success">成功</string>
<string name="kpm_control_failed">失敗</string>
<string name="home_click_to_ContributionCard_kernelsu">SukiSU Ultra は将来的に KSU から比較的に独立したブランチになりますが、公式の KernelSU や MKSU などの貢献に繋がるでしょう!</string>
<string name="not_supported">非対応</string>
<string name="supported">対応</string>
<string name="home_kpm_module">KPM モジュール数:%d </string>
<string name="kpm_invalid_file">無効な KPM ファイル</string>
<string name="kernel_patched">カーネルはパッチされていません</string>
<string name="kernel_not_enabled">カーネルは設定されていません</string>
<string name="custom_settings">カスタム設定</string>
<string name="kpm_install_mode">インストール</string>
<string name="kpm_install_mode_load">読み込む</string>
<string name="kpm_install_mode_embed">埋め込む</string>
<string name="kpm_install_mode_description">モジュールのインストールモードを選択してください:\n\n読み込む: モジュールを一時的に読み込みます\n埋め込む: システムに恒久的にインストールします</string>
<string name="log_failed_to_check_module_file">モジュールファイルの存在を確認できませんでした</string>
<string name="snackbar_failed_to_check_module_file">モジュールファイルが存在するか確認できません</string>
<string name="confirm_uninstall_title">アンインストールを確認</string>
<string name="confirm_uninstall_confirm">アンインストール中</string>
<string name="confirm_uninstall_dismiss">中止</string>
</resources> </resources>

View File

@@ -1,2 +1,265 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources></resources> <resources>
<string name="app_name" translatable="false">SukiSU Ultra</string>
<string name="home">Home</string>
<string name="home_not_installed">Chưa cài đặt</string>
<string name="home_click_to_install">Nhấn để cài đặt</string>
<string name="home_working">Đang làm việc</string>
<string name="home_working_version">Phiên bản: %d</string>
<string name="home_superuser_count">Superusers: %d</string>
<string name="home_module_count">Mô-đun: %d</string>
<string name="home_unsupported">Không được hỗ trợ</string>
<string name="home_unsupported_reason">Không phát hiện được trình điều khiển KernelSU trên kernel của bạn.</string>
<string name="home_kernel">Kernel</string>
<string name="home_susfs">SuSFS: %s</string>
<string name="home_susfs_version">Phiên bản SuSFS</string>
<string name="home_susfs_sus_su">SuS SU</string>
<string name="home_manager_version">Phiên bản quản lý</string>
<string name="home_fingerprint">Dấu vân tay</string>
<string name="home_selinux_status">Trạng thái SELinux</string>
<string name="selinux_status_disabled">Disabled</string>
<string name="selinux_status_enforcing">Enforcing</string>
<string name="selinux_status_permissive">Permissive</string>
<string name="selinux_status_unknown">Không rõ</string>
<string name="superuser">Superuser</string>
<string name="module_failed_to_enable">Không thể bật mô-đun: %s</string>
<string name="module_failed_to_disable">Không thể vô hiệu hóa mô-đun: %s</string>
<string name="module_empty">Không có mô-đun nào được cài đặt</string>
<string name="module">Mô-đun</string>
<string name="module_sort_action_first">Sắp xếp (theo hành động)</string>
<string name="module_sort_enabled_first">Sắp xếp (theo trạng thái)</string>
<string name="uninstall">Gỡ cài đặt</string>
<string name="restore">Khôi phục</string>
<string name="module_install">Cài đặt</string>
<string name="install">Cài đặt</string>
<string name="reboot">Khởi động lại</string>
<string name="settings">Cài đặt</string>
<string name="reboot_userspace">Khởi động lại không gian người dùng</string>
<string name="reboot_recovery">Khởi động lại vào Recovery</string>
<string name="reboot_bootloader">Khởi động lại vào Bootloader</string>
<string name="reboot_download">Khởi động lại vào Download mode</string>
<string name="reboot_edl">Khởi động lại vào EDL</string>
<string name="about">Thông tin</string>
<string name="module_uninstall_confirm">Bạn có chắc chắn muốn gỡ cài đặt mô-đun %s không?</string>
<string name="module_uninstall_success">%s đã gỡ cài đặt</string>
<string name="module_uninstall_failed">Không thể gỡ cài đặt: %s</string>
<string name="module_version">Phiên bản</string>
<string name="module_author">Tác giả</string>
<string name="refresh">Làm mới</string>
<string name="show_system_apps">Hiển thị ứng dụng hệ thống</string>
<string name="hide_system_apps">Ẩn ứng dụng hệ thống</string>
<string name="send_log">Gửi nhật ký</string>
<string name="safe_mode">Chế độ an toàn</string>
<string name="reboot_to_apply">Khởi động lại để có hiệu lực</string>
<string name="module_magisk_conflict">Các mô-đun không khả dụng do xung đột với Magisk!</string>
<string name="home_learn_kernelsu">Tìm hiểu KernelSU</string>
<string name="home_learn_kernelsu_url">https://kernelsu.org/guide/what-is-kernelsu.html</string>
<string name="home_click_to_learn_kernelsu">Tìm hiểu cách cài đặt KernelSU và sử dụng các mô-đun</string>
<string name="home_support_title">Hỗ trợ chúng tôi</string>
<string name="home_support_content">KernelSU sẽ luôn là miễn phí và mã nguồn mở. Tuy nhiên, bạn có thể cho chúng tôi thấy rằng bạn quan tâm bằng cách quyên góp.</string>
<string name="about_source_code"><![CDATA[View source code at %1$s<br/>Tham gia kênh %2$s của chúng tôi]]></string>
<string name="profile" translatable="false">Hồ sơ ứng dụng</string>
<string name="profile_default">Mặc định</string>
<string name="profile_template">Bản mẫu</string>
<string name="profile_custom">Tùy chỉnh</string>
<string name="profile_name">Tên hồ sơ</string>
<string name="profile_namespace">Tên không gian gắn kết</string>
<string name="profile_namespace_inherited">Được thừa hưởng</string>
<string name="profile_namespace_global">Toàn cầu</string>
<string name="profile_namespace_individual">Cá nhân</string>
<string name="profile_groups">Nhóm</string>
<string name="profile_capabilities">Khả năng</string>
<string name="profile_selinux_context">Bối cảnh SELinux</string>
<string name="profile_umount_modules">Bỏ gắn kết mô-đun</string>
<string name="failed_to_update_app_profile">Không cập nhật được Hồ sơ ứng dụng cho %s</string>
<string name="require_kernel_version" formatted="false">Phiên bản KernelSU hiện tại %d quá thấp để trình quản lý hoạt động bình thường. Vui lòng nâng cấp lên phiên bản %d hoặc cao hơn!</string>
<string name="settings_umount_modules_default">Bỏ gắn kết các mô-đun theo mặc định</string>
<string name="settings_umount_modules_default_summary">Giá trị mặc định cho \"Bỏ gắn kết các mô-đun\" trong Hồ sơ ứng dụng. Nếu được bật, nó sẽ xóa tất cả các sửa đổi của mô-đun đối với hệ thống đối và các ứng dụng không có hồ sơ được đặt.</string>
<string name="settings_susfs_toggle">Ẩn móc kprobe</string>
<string name="settings_susfs_toggle_summary">Vô hiệu hóa các móc kprobe do KSU tạo ra và kích hoạt các móc không phải kprobe tích hợp sẵn, triển khai sẽ được áp dụng cho hạt nhân không phải GKI, không hỗ trợ krp</string>
<string name="profile_umount_modules_summary">Bật tùy chọn này sẽ cho phép KernelSU khôi phục mọi tệp đã được các mô-đun sửa đổi cho ứng dụng này.</string>
<string name="profile_selinux_domain">Domain</string>
<string name="profile_selinux_rules">Quy tắc</string>
<string name="module_update">Cập nhật</string>
<string name="module_downloading">Đang tải xuống mô-đun: %s</string>
<string name="module_start_downloading">Bắt đầu tải xuống: %s</string>
<string name="new_version_available">Phiên bản mới %s đã có sẵn, hãy nhấp để cập nhật.</string>
<string name="launch_app">Khởi chạy</string>
<string name="force_stop_app" formatted="false">Buộc dừng</string>
<string name="restart_app">Khởi động lại</string>
<string name="failed_to_update_sepolicy">Không cập nhật được quy tắc SELinux cho %s</string>
<string name="module_changelog">Nhật ký thay đổi</string>
<string name="settings_profile_template">Mẫu hồ sơ ứng dụng</string>
<string name="settings_profile_template_summary">Quản lý mẫu cục bộ và trực tuyến của Hồ sơ ứng dụng</string>
<string name="app_profile_template_create">Tạo mẫu</string>
<string name="app_profile_template_edit">Chỉnh sửa mẫu</string>
<string name="app_profile_template_id">ID</string>
<string name="app_profile_template_id_invalid">ID mẫu không hợp lệ</string>
<string name="app_profile_template_name">Tên</string>
<string name="app_profile_template_description">Miêu tả</string>
<string name="app_profile_template_save">Lưu</string>
<string name="app_profile_template_delete">Xoá</string>
<string name="app_profile_template_view">Xem mẫu</string>
<string name="app_profile_template_readonly">Chỉ đọc</string>
<string name="app_profile_template_id_exist">ID mẫu đã tồn tại!</string>
<string name="app_profile_import_export">Nhập/Xuất</string>
<string name="app_profile_import_from_clipboard">Nhập từ bộ nhớ tạm clipboard</string>
<string name="app_profile_export_to_clipboard">Xuất vào bộ nhớ tạm clipboard</string>
<string name="app_profile_template_export_empty">Cannot find local template to export!</string>
<string name="app_profile_template_import_success">Đã nhập thành công</string>
<string name="app_profile_template_sync">Đồng bộ mẫu trực tuyến</string>
<string name="app_profile_template_save_failed">Không lưu được mẫu</string>
<string name="app_profile_template_import_empty">Bộ nhớ tạm đang trống!</string>
<string name="module_changelog_failed">Lấy nhật ký thay đổi không thành công: %s</string>
<string name="settings_check_update">Kiểm tra cập nhật</string>
<string name="settings_check_update_summary">Tự động kiểm tra cập nhật khi mở ứng dụng</string>
<string name="grant_root_failed">Không cấp được quyền root!</string>
<string name="action">Khởi chạy</string>
<string name="open">Mở</string>
<string name="enable_web_debugging">Bật gỡ lỗi WebView</string>
<string name="enable_web_debugging_summary">Có thể sử dụng để gỡ lỗi WebUI. Vui lòng chỉ bật khi cần thiết.</string>
<string name="direct_install">Cài đặt trực tiếp (Khuyến nghị)</string>
<string name="select_file">Chọn một tập tin</string>
<string name="install_inactive_slot">Cài đặt vào khe không hoạt động (Sau OTA)</string>
<string name="install_inactive_slot_warning">Thiết bị của bạn sẽ **BUỘC** phải khởi động vào khe cắm hiện tại không hoạt động sau khi khởi động lại!\nChỉ sử dụng tùy chọn này sau khi OTA hoàn tất.\nTiếp tục?</string>
<string name="install_next">Kế tiếp</string>
<string name="select_file_tip">Phân vùng %1$s được khuyến nghị</string>
<string name="select_kmi">Chọn KMI</string>
<string name="settings_uninstall">Uninstall</string>
<string name="settings_uninstall_temporary">Gỡ cài đặt tạm thời</string>
<string name="settings_uninstall_permanent">Gỡ cài đặt vĩnh viễn</string>
<string name="settings_restore_stock_image">Khôi phục img ban đầu</string>
<string name="settings_uninstall_temporary_message">Gỡ cài đặt tạm thời KernelSU, khôi phục lại trạng thái ban đầu sau lần khởi động lại tiếp theo.</string>
<string name="settings_uninstall_permanent_message">Gỡ cài đặt KernelSU (Root và tất cả các mô-đun) hoàn toàn và vĩnh viễn.</string>
<string name="settings_restore_stock_image_message">Khôi phục ảnh gốc (nếu có bản sao lưu), thường được sử dụng trước OTA; nếu bạn cần gỡ cài đặt KernelSU, vui lòng sử dụng \"Gỡ cài đặt vĩnh viễn\".</string>
<string name="flashing">Đang flash...</string>
<string name="flash_success">Flash thành công</string>
<string name="flash_failed">Lỗi flash</string>
<string name="selected_lkm">LKM đã chọn: %s</string>
<string name="save_log">Lưu nhật ký</string>
<string name="log_saved">Nhật ký đã lưu</string>
<string name="status_supported">Được hỗ trợ</string>
<string name="status_not_supported">Không được hỗ trợ</string>
<string name="status_unknown">Không rõ</string>
<string name="sus_su_mode">Chế độ SuS: SU</string>
<!-- Module related -->
<string name="module_install_confirm">Xác nhận cài đặt mô-đun %1$s ?</string>
<string name="unknown_module">Mô-đun không xác định</string>
<!-- Restore related -->
<string name="restore_confirm_title">Xác nhận khôi phục mô-đun</string>
<string name="restore_confirm_message">Hành động này sẽ ghi đè lên tất cả các mô-đun hiện có. Tiếp tục?</string>
<string name="confirm">Xác nhận</string>
<string name="cancel">Hủy bỏ</string>
<!-- Backup related -->
<string name="backup_success">Sao lưu thành công (tar.gz)</string>
<string name="backup_failed">Sao lưu không thành công: %1$s</string>
<string name="backup_modules">Sao lưu mô-đun</string>
<string name="restore_modules">Khôi phục các mô-đun</string>
<!-- Restore related messages -->
<string name="restore_success">Các mô-đun đã được khôi phục thành công, cần khởi động lại</string>
<string name="restore_failed">Khôi phục không thành công: %1$s</string>
<string name="restart_now">Khởi động lại ngay</string>
<string name="unknown_error">Lỗi không xác định</string>
<!-- Command related -->
<string name="command_execution_failed">Thực hiện lệnh không thành công: %1$s</string>
<!-- Allowlist related -->
<string name="allowlist_backup_success">Sao lưu danh sách cho phép thành công</string>
<string name="allowlist_backup_failed">Sao lưu danh sách cho phép không thành công: %1$s</string>
<string name="allowlist_restore_confirm_title">Xác nhận khôi phục danh sách cho phép</string>
<string name="allowlist_restore_confirm_message">Hành động này sẽ ghi đè lên danh sách cho phép hiện tại. Tiếp tục?</string>
<string name="allowlist_restore_success">Đã khôi phục danh sách cho phép thành công</string>
<string name="allowlist_restore_failed">Khôi phục danh sách cho phép không thành công: %1$s</string>
<string name="backup_allowlist">Sao lưu danh sách cho phép</string>
<string name="restore_allowlist">Khôi phục danh sách cho phép</string>
<string name="settings_custom_background">Cài đặt nền tùy chỉnh</string>
<string name="settings_custom_background_summary">Cài đặt tùy chỉnh nền</string>
<string name="settings_card_manage">Quản lý thẻ</string>
<string name="settings_card_alpha">Thẻ alpha</string>
<string name="settings_restore_default">Khôi phục mặc định</string>
<string name="home_android_version">Phiên bản Android</string>
<string name="home_device_model">Model thiết bị</string>
<string name="su_not_allowed">Không được phép cấp siêu người dùng cho %s</string>
<string name="settings_disable_su">Vô hiệu hóa khả năng tương thích su</string>
<string name="settings_disable_su_summary">Tạm thời vô hiệu hóa mọi ứng dụng khỏi quyền root thông qua lệnh su (các tiến trình root hiện có sẽ không bị ảnh hưởng).</string>
<string name="using_mksu_manager">Bạn đang sử dụng trình quản lý SukiSU thử nghiệm</string>
<string name="module_install_multiple_confirm">Bạn có chắc chắn muốn cài đặt các mô-đun %d đã chọn không?</string>
<string name="module_install_multiple_confirm_with_names">Bạn có chắc chắn muốn cài đặt các mô-đun %1$d sau không? \n\n%2$s</string>
<string name="more_settings">Nhiều thiết lập hơn</string>
<string name="selinux">SELinux</string>
<string name="selinux_enabled">Đã bật</string>
<string name="selinux_disabled">Đã tắt</string>
<string name="simple_mode">Chế độ đơn giản</string>
<string name="simple_mode_summary">Ẩn các thẻ không cần thiết khi bật</string>
<string name="hide_kernel_kernelsu_version">Ẩn phiên bản kernel</string>
<string name="hide_kernel_kernelsu_version_summary">Ẩn phiên bản kernel hiện tại</string>
<string name="hide_other_info">Ẩn thông tin thêm</string>
<string name="hide_other_info_summary">Ẩn thông tin về số lượng app đã được cấp root, mô-đun và mô-đun KPM trên trang chủ</string>
<string name="hide_susfs_status">Ẩn trạng thái SuSFS</string>
<string name="hide_susfs_status_summary">Ẩn thông tin trạng thái SuSFS trên trang chủ</string>
<string name="theme_mode">Chế độ chủ đề</string>
<string name="theme_follow_system">Theo hệ thống</string>
<string name="theme_light">Sáng</string>
<string name="theme_dark">Tối</string>
<string name="manual_hook">Móc thủ công</string>
<string name="dynamic_color_title">Màu sắc động</string>
<string name="dynamic_color_summary">Màu sắc động sử dụng chủ đề hệ thống</string>
<string name="choose_theme_color">Chọn màu chủ đề</string>
<string name="color_default">Trắng</string>
<string name="color_blue">Xanh dương</string>
<string name="color_green">Xanh lá</string>
<string name="color_purple">Tím</string>
<string name="color_orange">Cam</string>
<string name="color_pink">Hồng</string>
<string name="color_gray">Xám</string>
<string name="color_ivory">Ngà voi</string>
<string name="flash_option">Tùy chọn cọ</string>
<string name="flash_option_tip">Chọn tập tin cần flash</string>
<string name="horizon_kernel">File Anykernel3</string>
<string name="root_required">Yêu cầu quyền root</string>
<string name="copy_failed">Sao chép tập tin không thành công</string>
<string name="reboot_complete_title">Hoàn tất</string>
<string name="reboot_complete_msg">khởi động lại ngay lập tức?</string>
<string name="yes"></string>
<string name="no">Không</string>
<string name="failed_reboot">Khởi động lại không thành công</string>
<string name="batch_authorization">Giấy phép số lượng lớn</string>
<string name="batch_cancel_authorization">Hủy ủy quyền hàng loạt</string>
<string name="backup">Sao lưu</string>
<string name="color_yellow">Vàng</string>
<string name="kpm">Mô-đun kpm</string>
<string name="kpm_title">Mô-đun kernel</string>
<string name="kpm_empty">Không có mô-đun kernel nào được cài đặt tại thời điểm này</string>
<string name="kpm_version">Phát hành</string>
<string name="kpm_author">Tác giả</string>
<string name="kpm_uninstall">Gỡ cài đặt</string>
<string name="kpm_uninstall_success">Đã gỡ cài đặt thành công</string>
<string name="kpm_uninstall_failed">Không thể gỡ cài đặt</string>
<string name="kpm_install">Chọn Cài đặt</string>
<string name="kpm_install_success">Tải module kpm thành công</string>
<string name="kpm_install_failed">Tải module kpm không thành công</string>
<string name="kpm_args">thông số kpm</string>
<string name="kpm_control">fulfillment</string>
<string name="home_kpm_version">Phiên bản KPM</string>
<string name="close_notice">đóng</string>
<string name="kernel_module_notice">Các chức năng mô-đun kernel sau đây được KernelPatch phát triển và sửa đổi để bao gồm các chức năng mô-đun hạt nhân của SukiSU Ultra</string>
<string name="home_ContributionCard_kernelsu">SukiSU Ultra Mong đợi</string>
<string name="kpm_control_success">thành công</string>
<string name="kpm_control_failed">thất bại</string>
<string name="home_click_to_ContributionCard_kernelsu">SukiSU Ultra sẽ là một nhánh tương đối độc lập của KSU trong tương lai, nhưng xin cảm ơn KernelSU và MKSU chính thức vì những đóng góp của họ!</string>
<string name="not_supported">không được hỗ trợ</string>
<string name="supported">được hỗ trợ</string>
<string name="home_kpm_module">Số lượng mô-đun KPM%d </string>
<string name="kpm_invalid_file">Tệp KPM không hợp lệ</string>
<string name="kernel_patched">Kernel chưa được vá</string>
<string name="kernel_not_enabled">Kernel chưa được cấu hình</string>
<string name="custom_settings">Cài đặt tùy chỉnh</string>
<string name="kpm_install_mode">cài đặt</string>
<string name="kpm_install_mode_load">tải</string>
<string name="kpm_install_mode_embed">nhúng</string>
<string name="kpm_install_mode_description">Vui lòng chọn chế độ cài đặt mô-đun: \n\nTải: Tải tạm thời mô-đun \nNhúng: Cài đặt vĩnh viễn vào hệ thống</string>
<string name="log_failed_to_check_module_file">Không kiểm tra được sự tồn tại của tệp mô-đun</string>
<string name="snackbar_failed_to_check_module_file">Không thể kiểm tra xem tệp mô-đun có tồn tại không</string>
<string name="confirm_uninstall_title">Xác nhận gỡ cài đặt</string>
<string name="confirm_uninstall_confirm">loại bỏ</string>
<string name="confirm_uninstall_dismiss">bãi bỏ</string>
</resources>

View File

@@ -230,11 +230,9 @@
<string name="kpm_version">版本</string> <string name="kpm_version">版本</string>
<string name="kpm_author">作者</string> <string name="kpm_author">作者</string>
<string name="kpm_uninstall">卸载</string> <string name="kpm_uninstall">卸载</string>
<string name="kpm_uninstall_confirm">确定要卸载内核模块 %1$s 吗?</string>
<string name="kpm_uninstall_success">卸载成功</string> <string name="kpm_uninstall_success">卸载成功</string>
<string name="kpm_uninstall_failed">卸载失败</string> <string name="kpm_uninstall_failed">卸载失败</string>
<string name="kpm_install">加载 kpm 模块</string> <string name="kpm_install">选择安装</string>
<string name="kpm_install_confirm">确认加载吗?</string>
<string name="kpm_install_success">加载 kpm 模块成功</string> <string name="kpm_install_success">加载 kpm 模块成功</string>
<string name="kpm_install_failed">加载 kpm 模块失败</string> <string name="kpm_install_failed">加载 kpm 模块失败</string>
<string name="home_kpm_version">KPM 版本</string> <string name="home_kpm_version">KPM 版本</string>
@@ -252,4 +250,12 @@
<string name="home_ContributionCard_kernelsu">SukiSU Ultra 展望</string> <string name="home_ContributionCard_kernelsu">SukiSU Ultra 展望</string>
<string name="home_click_to_ContributionCard_kernelsu">SukiSU Ultra 未来将会成为一个相对独立的 KSU 分支,但是依然感谢官方 KernelSU 和 MKSU 等做出的贡献</string> <string name="home_click_to_ContributionCard_kernelsu">SukiSU Ultra 未来将会成为一个相对独立的 KSU 分支,但是依然感谢官方 KernelSU 和 MKSU 等做出的贡献</string>
<string name="custom_settings">个性化设置</string> <string name="custom_settings">个性化设置</string>
<string name="kpm_install_mode">安装</string>
<string name="kpm_install_mode_load">加载</string>
<string name="kpm_install_mode_embed">嵌入</string>
<string name="kpm_install_mode_description">请选择模块安装模式:\n\n加载临时加载模块\n嵌入永久安装到系统</string>
<string name="snackbar_failed_to_check_module_file">无法检查模块文件是否存在</string>
<string name="confirm_uninstall_title">确认卸载</string>
<string name="confirm_uninstall_confirm">删除</string>
<string name="confirm_uninstall_dismiss">取消</string>
</resources> </resources>

View File

@@ -232,11 +232,9 @@
<string name="kpm_version">releases</string> <string name="kpm_version">releases</string>
<string name="kpm_author">author</string> <string name="kpm_author">author</string>
<string name="kpm_uninstall">uninstallation</string> <string name="kpm_uninstall">uninstallation</string>
<string name="kpm_uninstall_confirm">Determine the kernel module to uninstall: %1$s </string>
<string name="kpm_uninstall_success">Uninstalled successfully</string> <string name="kpm_uninstall_success">Uninstalled successfully</string>
<string name="kpm_uninstall_failed">Failed to uninstall</string> <string name="kpm_uninstall_failed">Failed to uninstall</string>
<string name="kpm_install">Load the kpm module</string> <string name="kpm_install">Select Installation</string>
<string name="kpm_install_confirm">Confirm Load?</string>
<string name="kpm_install_success">Load of kpm module successful</string> <string name="kpm_install_success">Load of kpm module successful</string>
<string name="kpm_install_failed">Load of kpm module failed</string> <string name="kpm_install_failed">Load of kpm module failed</string>
<string name="kpm_args">kpm parameters</string> <string name="kpm_args">kpm parameters</string>
@@ -255,4 +253,13 @@
<string name="kernel_patched">Kernel not patched</string> <string name="kernel_patched">Kernel not patched</string>
<string name="kernel_not_enabled">Kernel not configured</string> <string name="kernel_not_enabled">Kernel not configured</string>
<string name="custom_settings">Custom settings</string> <string name="custom_settings">Custom settings</string>
<string name="kpm_install_mode">install</string>
<string name="kpm_install_mode_load">Load</string>
<string name="kpm_install_mode_embed">embed</string>
<string name="kpm_install_mode_description">Please select the module installation mode: \n\nLoad: Temporarily load the module \nEmbedded: Permanently install into the system</string>
<string name="log_failed_to_check_module_file">Failed to check module file existence</string>
<string name="snackbar_failed_to_check_module_file">Unable to check if module file exists</string>
<string name="confirm_uninstall_title">Confirm uninstallation</string>
<string name="confirm_uninstall_confirm">removing</string>
<string name="confirm_uninstall_dismiss">abolish</string>
</resources> </resources>

View File

@@ -6,6 +6,7 @@ edition = "2024"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
notify = "6.1"
anyhow = "1" anyhow = "1"
clap = { version = "4", features = ["derive"] } clap = { version = "4", features = ["derive"] }
const_format = "0.2" const_format = "0.2"

View File

@@ -5,10 +5,13 @@ use anyhow::{Context, Result};
use log::{info, warn}; use log::{info, warn};
use rustix::fs::{MountFlags, mount}; use rustix::fs::{MountFlags, mount};
use std::path::Path; use std::path::Path;
use crate::kpm;
pub fn on_post_data_fs() -> Result<()> { pub fn on_post_data_fs() -> Result<()> {
ksucalls::report_post_fs_data(); ksucalls::report_post_fs_data();
kpm::start_kpm_watcher()?;
utils::umask(0); utils::umask(0);
#[cfg(unix)] #[cfg(unix)]
@@ -98,6 +101,13 @@ pub fn on_post_data_fs() -> Result<()> {
run_stage("post-mount", true); run_stage("post-mount", true);
for entry in std::fs::read_dir(kpm::KPM_DIR)? {
let path = entry?.path();
if path.extension().is_some_and(|ext| ext == "kpm") {
let _ = kpm::load_kpm(&path);
}
}
Ok(()) Ok(())
} }

122
userspace/ksud/src/kpm.rs Normal file
View File

@@ -0,0 +1,122 @@
use anyhow::Result;
use notify::{Watcher, RecursiveMode};
use std::path::Path;
use std::fs;
use anyhow::anyhow;
pub const KPM_DIR: &str = "/data/adb/kpm";
pub const KPMMGR_PATH: &str = "/data/adb/ksu/bin/kpmmgr";
pub fn ensure_kpm_dir() -> Result<()> {
if !Path::new(KPM_DIR).exists() {
fs::create_dir_all(KPM_DIR)?;
}
Ok(())
}
pub fn start_kpm_watcher() -> Result<()> {
ensure_kpm_dir()?;
load_existing_kpms()?;
// 检查是否处于安全模式
if crate::utils::is_safe_mode() {
log::warn!("The system is in safe mode and is deleting all KPM modules...");
if let Err(e) = remove_all_kpms() {
log::error!("Error deleting all KPM modules: {}", e);
}
return Ok(());
}
let mut watcher = notify::recommended_watcher(|res| {
match res {
Ok(event) => handle_kpm_event(event),
Err(e) => log::error!("monitoring error: {:?}", e),
}
})?;
watcher.watch(Path::new(KPM_DIR), RecursiveMode::NonRecursive)?;
Ok(())
}
pub fn handle_kpm_event(event: notify::Event) {
match event.kind {
notify::EventKind::Create(_) => {
event.paths.iter().for_each(|path| {
if path.extension().is_some_and(|ext| ext == "kpm") {
let _ = load_kpm(path);
}
});
}
notify::EventKind::Remove(_) => {
event.paths.iter().for_each(|path| {
if let Some(name) = path.file_stem() {
let _ = unload_kpm(name.to_string_lossy().as_ref());
}
});
}
_ => {}
}
}
pub fn load_kpm(path: &Path) -> Result<()> {
let status = std::process::Command::new(KPMMGR_PATH)
.args(["load", path.to_str().unwrap(), ""])
.status()?;
if status.success() {
log::info!("Loaded KPM: {}", path.display());
}
Ok(())
}
pub fn unload_kpm(name: &str) -> Result<()> {
let status = std::process::Command::new(KPMMGR_PATH)
.args(["unload", name])
.status()
.map_err(|e| anyhow!("Failed to execute kpmmgr: {}", e))?;
let kpm_path = Path::new(KPM_DIR).join(format!("{}.kpm", name));
if kpm_path.exists() {
fs::remove_file(&kpm_path)
.map_err(|e| anyhow!("Failed to delete KPM file: {}", e))
.map(|_| log::info!("Deleted KPM file: {}", kpm_path.display()))?;
}
if status.success() {
log::info!("Successfully unloaded KPM: {}", name);
} else {
log::warn!("KPM unloading may have failed: {}", name);
}
Ok(())
}
pub fn remove_all_kpms() -> Result<()> {
ensure_kpm_dir()?;
for entry in fs::read_dir(KPM_DIR)? {
let path = entry?.path();
if path.extension().is_some_and(|ext| ext == "kpm") {
if let Some(name) = path.file_stem() {
unload_kpm(name.to_string_lossy().as_ref())
.unwrap_or_else(|e| log::error!("Failed to remove KPM: {}", e));
let _ = fs::remove_file(&path);
}
}
}
Ok(())
}
// 加载所有现有的 KPM 模块
pub fn load_existing_kpms() -> Result<()> {
ensure_kpm_dir()?;
for entry in fs::read_dir(KPM_DIR)? {
let path = entry?.path();
if path.extension().map_or(false, |ext| ext == "kpm") {
let _ = load_kpm(&path);
}
}
Ok(())
}

View File

@@ -14,6 +14,7 @@ mod restorecon;
mod sepolicy; mod sepolicy;
mod su; mod su;
mod utils; mod utils;
mod kpm;
fn main() -> anyhow::Result<()> { fn main() -> anyhow::Result<()> {
cli::run() cli::run()