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

View File

@@ -24,6 +24,7 @@
<string name="module_sort_action_first">排序(可执行优先)</string> <string name="module_sort_action_first">排序(可执行优先)</string>
<string name="module_sort_enabled_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>

View File

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

View File

@@ -204,6 +204,12 @@ enum Module {
id: String, id: String,
}, },
/// Restore module <id>
Restore {
/// module id
id: String,
},
/// enable module <id> /// enable module <id>
Enable { Enable {
/// module id /// module id
@@ -303,6 +309,7 @@ pub fn run() -> Result<()> {
match command { match command {
Module::Install { zip } => module::install_module(&zip), Module::Install { zip } => module::install_module(&zip),
Module::Uninstall { id } => module::uninstall_module(&id), Module::Uninstall { id } => module::uninstall_module(&id),
Module::Restore { id } => module::restore_uninstall_module(&id),
Module::Enable { id } => module::enable_module(&id), Module::Enable { id } => module::enable_module(&id),
Module::Disable { id } => module::disable_module(&id), Module::Disable { id } => module::disable_module(&id),
Module::Action { id } => module::run_action(&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) 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<()> { pub fn run_action(id: &str) -> Result<()> {
let action_script_path = format!("/data/adb/modules/{}/action.sh", id); let action_script_path = format!("/data/adb/modules/{}/action.sh", id);
exec_script(&action_script_path, true) 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); 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() { if !module_prop_map.contains_key("id") || module_prop_map["id"].is_empty() {
match entry.file_name().to_str() { info!("Use dir name as module id: {dir_id}");
Some(id) => { module_prop_map.insert("id".to_owned(), dir_id.clone());
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;
}
}
} }
// Add enabled, update, remove flags // Add enabled, update, remove flags