allow restore uninstalled module
This commit is contained in:
@@ -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)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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),
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user