diff --git a/manager/app/src/main/java/com/rifsxd/ksunext/ui/screen/Module.kt b/manager/app/src/main/java/com/rifsxd/ksunext/ui/screen/Module.kt
index d26ec503..a078c240 100644
--- a/manager/app/src/main/java/com/rifsxd/ksunext/ui/screen/Module.kt
+++ b/manager/app/src/main/java/com/rifsxd/ksunext/ui/screen/Module.kt
@@ -31,10 +31,8 @@ import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.selection.toggleable
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.outlined.Wysiwyg
-import androidx.compose.material.icons.filled.Add
-import androidx.compose.material.icons.outlined.PlayArrow
-import androidx.compose.material.icons.outlined.Download
-import androidx.compose.material.icons.outlined.Delete
+import androidx.compose.material.icons.filled.*
+import androidx.compose.material.icons.outlined.*
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
@@ -101,6 +99,7 @@ import com.rifsxd.ksunext.ui.util.hasMagisk
import com.rifsxd.ksunext.ui.util.reboot
import com.rifsxd.ksunext.ui.util.toggleModule
import com.rifsxd.ksunext.ui.util.uninstallModule
+import com.rifsxd.ksunext.ui.util.restoreModule
import com.rifsxd.ksunext.ui.viewmodel.ModuleViewModel
import com.rifsxd.ksunext.ui.webui.WebUIActivity
import okhttp3.OkHttpClient
@@ -255,13 +254,17 @@ private fun ModuleList(
val failedEnable = stringResource(R.string.module_failed_to_enable)
val failedDisable = stringResource(R.string.module_failed_to_disable)
val failedUninstall = stringResource(R.string.module_uninstall_failed)
+ val failedRestore = stringResource(R.string.module_restore_failed)
val successUninstall = stringResource(R.string.module_uninstall_success)
+ val successRestore = stringResource(R.string.module_restore_success)
val reboot = stringResource(R.string.reboot)
val rebootToApply = stringResource(R.string.reboot_to_apply)
val moduleStr = stringResource(R.string.module)
val uninstall = stringResource(R.string.uninstall)
+ val restore = stringResource(R.string.restore)
val cancel = stringResource(android.R.string.cancel)
val moduleUninstallConfirm = stringResource(R.string.module_uninstall_confirm)
+ val moduleRestoreConfirm = stringResource(R.string.module_restore_confirm)
val updateText = stringResource(R.string.module_update)
val changelogText = stringResource(R.string.module_changelog)
val downloadingText = stringResource(R.string.module_downloading)
@@ -375,6 +378,33 @@ private fun ModuleList(
reboot()
}
}
+
+ suspend fun onModuleRestore(module: ModuleViewModel.ModuleInfo) {
+ val confirmResult = confirmDialog.awaitConfirm(
+ moduleStr,
+ content = moduleRestoreConfirm.format(module.name),
+ confirm = restore,
+ dismiss = cancel
+ )
+ if (confirmResult != ConfirmResult.Confirmed) {
+ return
+ }
+
+ val success = loadingDialog.withLoading {
+ withContext(Dispatchers.IO) {
+ restoreModule(module.id)
+ }
+ }
+
+ if (success) {
+ viewModel.fetchModuleList()
+ }
+ val message = if (success) {
+ successRestore.format(module.name)
+ } else {
+ failedRestore.format(module.name)
+ }
+ }
PullToRefreshBox(
modifier = boxModifier,
onRefresh = {
@@ -427,6 +457,9 @@ private fun ModuleList(
onUninstall = {
scope.launch { onModuleUninstall(module) }
},
+ onRestore = {
+ scope.launch { onModuleRestore(module) }
+ },
onCheckChanged = {
scope.launch {
val success = loadingDialog.withLoading {
@@ -486,6 +519,7 @@ fun ModuleItem(
isChecked: Boolean,
updateUrl: String,
onUninstall: (ModuleViewModel.ModuleInfo) -> Unit,
+ onRestore: (ModuleViewModel.ModuleInfo) -> Unit,
onCheckChanged: (Boolean) -> Unit,
onUpdate: (ModuleViewModel.ModuleInfo) -> Unit,
onClick: (ModuleViewModel.ModuleInfo) -> Unit
@@ -576,29 +610,28 @@ fun ModuleItem(
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
-
if (module.hasActionScript) {
FilledTonalButton(
modifier = Modifier.defaultMinSize(52.dp, 32.dp),
onClick = {
- navigator.navigate(ExecuteModuleActionScreenDestination(module.id))
- viewModel.markNeedRefresh()
+ navigator.navigate(ExecuteModuleActionScreenDestination(module.id))
+ viewModel.markNeedRefresh()
},
contentPadding = ButtonDefaults.TextButtonContentPadding
) {
Icon(
- modifier = Modifier.size(20.dp),
+ modifier = Modifier.size(20.dp),
imageVector = Icons.Outlined.PlayArrow,
contentDescription = null
)
- if (!module.hasWebUi && updateUrl.isEmpty()) {
+ if (!module.hasWebUi && updateUrl.isEmpty()) {
Text(
- modifier = Modifier.padding(start = 7.dp),
+ modifier = Modifier.padding(start = 7.dp),
text = stringResource(R.string.action),
fontFamily = MaterialTheme.typography.labelMedium.fontFamily,
fontSize = MaterialTheme.typography.labelMedium.fontSize
)
- }
+ }
}
Spacer(modifier = Modifier.weight(0.1f, true))
@@ -612,16 +645,16 @@ fun ModuleItem(
contentPadding = ButtonDefaults.TextButtonContentPadding
) {
Icon(
- modifier = Modifier.size(20.dp),
+ modifier = Modifier.size(20.dp),
imageVector = Icons.AutoMirrored.Outlined.Wysiwyg,
contentDescription = null
)
if (!module.hasActionScript && updateUrl.isEmpty()) {
Text(
- modifier = Modifier.padding(start = 7.dp),
- fontFamily = MaterialTheme.typography.labelMedium.fontFamily,
- fontSize = MaterialTheme.typography.labelMedium.fontSize,
- text = stringResource(R.string.open)
+ modifier = Modifier.padding(start = 7.dp),
+ fontFamily = MaterialTheme.typography.labelMedium.fontFamily,
+ fontSize = MaterialTheme.typography.labelMedium.fontSize,
+ text = stringResource(R.string.open)
)
}
}
@@ -636,43 +669,61 @@ fun ModuleItem(
shape = ButtonDefaults.textShape,
contentPadding = ButtonDefaults.TextButtonContentPadding
) {
- Icon(
+ Icon(
modifier = Modifier.size(20.dp),
imageVector = Icons.Outlined.Download,
contentDescription = null
- )
- if (!module.hasActionScript || !module.hasWebUi) {
+ )
+ if (!module.hasActionScript || !module.hasWebUi) {
Text(
- modifier = Modifier.padding(start = 7.dp),
+ modifier = Modifier.padding(start = 7.dp),
fontFamily = MaterialTheme.typography.labelMedium.fontFamily,
fontSize = MaterialTheme.typography.labelMedium.fontSize,
text = stringResource(R.string.module_update)
)
- }
+ }
}
Spacer(modifier = Modifier.weight(0.1f, true))
}
- FilledTonalButton(
- modifier = Modifier.defaultMinSize(52.dp, 32.dp),
- enabled = !module.remove,
- onClick = { onUninstall(module) },
- contentPadding = ButtonDefaults.TextButtonContentPadding
- ) {
- Icon(
- modifier = Modifier.size(20.dp),
- imageVector = Icons.Outlined.Delete,
- contentDescription = null
- )
- if (!module.hasActionScript && !module.hasWebUi && updateUrl.isEmpty()) {
- Text(
- modifier = Modifier.padding(start = 7.dp),
+ if (module.remove) {
+ FilledTonalButton(
+ modifier = Modifier.defaultMinSize(52.dp, 32.dp),
+ onClick = { onRestore(module) },
+ contentPadding = ButtonDefaults.TextButtonContentPadding
+ ) {
+ Icon(
+ modifier = Modifier.size(20.dp),
+ imageVector = Icons.Outlined.Restore,
+ contentDescription = null
+ )
+ Text(
+ modifier = Modifier.padding(start = 7.dp),
+ fontFamily = MaterialTheme.typography.labelMedium.fontFamily,
+ fontSize = MaterialTheme.typography.labelMedium.fontSize,
+ text = stringResource(R.string.restore)
+ )
+ }
+ } else {
+ FilledTonalButton(
+ modifier = Modifier.defaultMinSize(52.dp, 32.dp),
+ enabled = true,
+ onClick = { onUninstall(module) },
+ contentPadding = ButtonDefaults.TextButtonContentPadding
+ ) {
+ Icon(
+ modifier = Modifier.size(20.dp),
+ imageVector = Icons.Outlined.Delete,
+ contentDescription = null
+ )
+ Text(
+ modifier = Modifier.padding(start = 7.dp),
fontFamily = MaterialTheme.typography.labelMedium.fontFamily,
fontSize = MaterialTheme.typography.labelMedium.fontSize,
text = stringResource(R.string.uninstall)
)
- }
+ }
}
}
}
@@ -696,5 +747,5 @@ fun ModuleItemPreview() {
hasWebUi = false,
hasActionScript = false
)
- ModuleItem(EmptyDestinationsNavigator, module, true, "", {}, {}, {}, {})
+ ModuleItem(EmptyDestinationsNavigator, module, true, "", {}, {}, {}, {}, {})
}
\ No newline at end of file
diff --git a/manager/app/src/main/java/com/rifsxd/ksunext/ui/util/KsuCli.kt b/manager/app/src/main/java/com/rifsxd/ksunext/ui/util/KsuCli.kt
index efc5a96c..b717efa8 100644
--- a/manager/app/src/main/java/com/rifsxd/ksunext/ui/util/KsuCli.kt
+++ b/manager/app/src/main/java/com/rifsxd/ksunext/ui/util/KsuCli.kt
@@ -142,6 +142,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,
diff --git a/manager/app/src/main/res/values/strings.xml b/manager/app/src/main/res/values/strings.xml
index de562fd9..786c17fb 100644
--- a/manager/app/src/main/res/values/strings.xml
+++ b/manager/app/src/main/res/values/strings.xml
@@ -27,6 +27,7 @@
Confirm Installation
Do you want to continue installing this module?\nModule: %1$s
Uninstall
+ Restore
Install
Install
Reboot
@@ -40,6 +41,9 @@
Are you sure you want to uninstall module %s?
%s uninstalled
Failed to uninstall: %s
+ Are you sure you want to restore module %s?
+ %s restoreed
+ Failed to restore: %s
Version
Author
Refresh
diff --git a/userspace/ksud/src/banner b/userspace/ksud/src/banner
index 6087569c..bf46a6ce 100644
--- a/userspace/ksud/src/banner
+++ b/userspace/ksud/src/banner
@@ -3,3 +3,7 @@
| ' // _ \ '__| '_ \ / _ \ \___ \| | | |
| . \ __/ | | | | | __/ |___) | |_| |
|_|\_\___|_| |_| |_|\___|_|____/ \___/
+ | \ | | _____ _| |_
+ | \| |/ _ \ \/ / __|
+ | |\ | __/> <| |_
+ |_| \_|\___/_/\_\\__|
diff --git a/userspace/ksud/src/cli.rs b/userspace/ksud/src/cli.rs
index 7d8f35ce..8cdb4a5b 100644
--- a/userspace/ksud/src/cli.rs
+++ b/userspace/ksud/src/cli.rs
@@ -143,7 +143,7 @@ enum Debug {
/// Set the manager app, kernel CONFIG_KSU_DEBUG should be enabled.
SetManager {
/// manager package name
- #[arg(default_value_t = String::from("me.weishu.kernelsu"))]
+ #[arg(default_value_t = String::from("com.rifsxd.ksunext"))]
apk: String,
},
@@ -204,6 +204,12 @@ enum Module {
id: String,
},
+ /// Restore module
+ Restore {
+ /// module id
+ id: String,
+ },
+
/// enable module
Enable {
/// module id
@@ -304,6 +310,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_module(&id),
Module::Enable { id } => module::enable_module(&id),
Module::Disable { id } => module::disable_module(&id),
Module::Action { id } => module::run_action(&id),
diff --git a/userspace/ksud/src/module.rs b/userspace/ksud/src/module.rs
index 4971c2f0..8c5c8c87 100644
--- a/userspace/ksud/src/module.rs
+++ b/userspace/ksud/src/module.rs
@@ -392,6 +392,10 @@ pub fn uninstall_module(id: &str) -> Result<()> {
mark_module_state(id, defs::REMOVE_FILE_NAME, true)
}
+pub fn restore_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)
diff --git a/userspace/ksud/src/utils.rs b/userspace/ksud/src/utils.rs
index 0bc13245..21033918 100644
--- a/userspace/ksud/src/utils.rs
+++ b/userspace/ksud/src/utils.rs
@@ -225,7 +225,7 @@ pub fn uninstall(magiskboot_path: Option) -> Result<()> {
boot_patch::restore(None, magiskboot_path, true)?;
println!("- Uninstall KernelSU manager..");
Command::new("pm")
- .args(["uninstall", "me.weishu.kernelsu"])
+ .args(["uninstall", "com.rifsxd.ksunext"])
.spawn()?;
println!("- Rebooting in 5 seconds..");
std::thread::sleep(std::time::Duration::from_secs(5));