allow restore uninstalled module

This commit is contained in:
5ec1cff
2025-01-26 23:55:02 +08:00
parent 7b39c295aa
commit ec383ed661
6 changed files with 64 additions and 31 deletions

View File

@@ -37,6 +37,7 @@ import androidx.compose.material.icons.outlined.PlayArrow
import androidx.compose.material.icons.outlined.Download
import androidx.compose.material.icons.outlined.Delete
import androidx.compose.material3.AlertDialog
import androidx.compose.material.icons.outlined.Refresh
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Checkbox
@@ -71,6 +72,7 @@ import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.rotate
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
@@ -104,6 +106,7 @@ import me.weishu.kernelsu.ui.util.LocalSnackbarHost
import me.weishu.kernelsu.ui.util.download
import me.weishu.kernelsu.ui.util.hasMagisk
import me.weishu.kernelsu.ui.util.reboot
import me.weishu.kernelsu.ui.util.restoreModule
import me.weishu.kernelsu.ui.util.toggleModule
import me.weishu.kernelsu.ui.util.uninstallModule
import me.weishu.kernelsu.ui.util.getFileName
@@ -398,7 +401,9 @@ private fun ModuleList(
}
}
suspend fun onModuleUninstall(module: ModuleViewModel.ModuleInfo) {
suspend fun onModuleUninstallClicked(module: ModuleViewModel.ModuleInfo) {
val isUninstall = !module.remove
if (isUninstall) {
val confirmResult = confirmDialog.awaitConfirm(
moduleStr,
content = moduleUninstallConfirm.format(module.name),
@@ -408,16 +413,22 @@ private fun ModuleList(
if (confirmResult != ConfirmResult.Confirmed) {
return
}
}
val success = loadingDialog.withLoading {
withContext(Dispatchers.IO) {
if (isUninstall) {
uninstallModule(module.dirId)
} else {
restoreModule(module.dirId)
}
}
}
if (success) {
viewModel.fetchModuleList()
}
if (!isUninstall) return
val message = if (success) {
successUninstall.format(module.name)
} else {
@@ -484,8 +495,8 @@ private fun ModuleList(
navigator = navigator,
module = module,
updateUrl = updatedModule.first,
onUninstall = {
scope.launch { onModuleUninstall(module) }
onUninstallClicked = {
scope.launch { onModuleUninstallClicked(module) }
},
onCheckChanged = {
scope.launch {
@@ -543,7 +554,7 @@ fun ModuleItem(
navigator: DestinationsNavigator,
module: ModuleViewModel.ModuleInfo,
updateUrl: String,
onUninstall: (ModuleViewModel.ModuleInfo) -> Unit,
onUninstallClicked: (ModuleViewModel.ModuleInfo) -> Unit,
onCheckChanged: (Boolean) -> Unit,
onUpdate: (ModuleViewModel.ModuleInfo) -> Unit,
onClick: (ModuleViewModel.ModuleInfo) -> Unit
@@ -731,21 +742,28 @@ fun ModuleItem(
FilledTonalButton(
modifier = Modifier.defaultMinSize(52.dp, 32.dp),
enabled = !module.remove,
onClick = { onUninstall(module) },
onClick = { onUninstallClicked(module) },
contentPadding = ButtonDefaults.TextButtonContentPadding
) {
if (!module.remove) {
Icon(
modifier = Modifier.size(20.dp),
imageVector = Icons.Outlined.Delete,
contentDescription = null
contentDescription = null,
)
} else {
Icon(
modifier = Modifier.size(20.dp).rotate(180f),
imageVector = Icons.Outlined.Refresh,
contentDescription = null,
)
}
if (!module.hasActionScript && !module.hasWebUi && updateUrl.isEmpty()) {
Text(
modifier = Modifier.padding(start = 7.dp),
fontFamily = MaterialTheme.typography.labelMedium.fontFamily,
fontSize = MaterialTheme.typography.labelMedium.fontSize,
text = stringResource(R.string.uninstall)
text = stringResource(if (!module.remove) R.string.uninstall else R.string.restore)
)
}
}

View File

@@ -147,6 +147,13 @@ fun uninstallModule(id: String): Boolean {
return result
}
fun restoreModule(id: String): Boolean {
val cmd = "module restore $id"
val result = execKsud(cmd, true)
Log.i(TAG, "restore module $id result: $result")
return result
}
private fun flashWithIO(
cmd: String,
onStdout: (String) -> Unit,

View File

@@ -24,6 +24,7 @@
<string name="module_sort_action_first">排序(可执行优先)</string>
<string name="module_sort_enabled_first">排序(已启用优先)</string>
<string name="uninstall">卸载</string>
<string name="restore">还原</string>
<string name="module_install">安装</string>
<string name="install">安装</string>
<string name="reboot">重启</string>

View File

@@ -28,6 +28,7 @@
<string name="module_sort_enabled_first">Sort (Enabled first)</string>
<string name="confirm">Confirm</string>
<string name="uninstall">Uninstall</string>
<string name="restore">Restore</string>
<string name="module_install">Install</string>
<string name="install">Install</string>
<string name="reboot">Reboot</string>

View File

@@ -204,6 +204,12 @@ enum Module {
id: String,
},
/// Restore module <id>
Restore {
/// module id
id: String,
},
/// enable module <id>
Enable {
/// module id
@@ -303,6 +309,7 @@ pub fn run() -> Result<()> {
match command {
Module::Install { zip } => module::install_module(&zip),
Module::Uninstall { id } => module::uninstall_module(&id),
Module::Restore { id } => module::restore_uninstall_module(&id),
Module::Enable { id } => module::enable_module(&id),
Module::Disable { id } => module::disable_module(&id),
Module::Action { id } => module::run_action(&id),

View File

@@ -394,6 +394,10 @@ pub fn uninstall_module(id: &str) -> Result<()> {
mark_module_state(id, defs::REMOVE_FILE_NAME, true)
}
pub fn restore_uninstall_module(id: &str) -> Result<()> {
mark_module_state(id, defs::REMOVE_FILE_NAME, false)
}
pub fn run_action(id: &str) -> Result<()> {
let action_script_path = format!("/data/adb/modules/{}/action.sh", id);
exec_script(&action_script_path, true)
@@ -456,17 +460,12 @@ fn _list_modules(path: &str) -> Vec<HashMap<String, String>> {
module_prop_map.insert(k, v);
});
let dir_id = entry.file_name().to_string_lossy().to_string();
module_prop_map.insert("dir_id".to_owned(), dir_id.clone());
if !module_prop_map.contains_key("id") || module_prop_map["id"].is_empty() {
match entry.file_name().to_str() {
Some(id) => {
info!("Use dir name as module id: {}", id);
module_prop_map.insert("id".to_owned(), id.to_owned());
}
_ => {
info!("Failed to get module id: {:?}", module_prop);
continue;
}
}
info!("Use dir name as module id: {dir_id}");
module_prop_map.insert("id".to_owned(), dir_id.clone());
}
// Add enabled, update, remove flags