diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/AppProfile.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/AppProfile.kt
index 7e91bfd5..e06ef0d3 100644
--- a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/AppProfile.kt
+++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/AppProfile.kt
@@ -3,7 +3,9 @@ package me.weishu.kernelsu.ui.screen
import android.util.Log
import androidx.annotation.StringRes
import androidx.compose.animation.Crossfade
+import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
@@ -20,6 +22,8 @@ import androidx.compose.material.icons.filled.ArrowDropDown
import androidx.compose.material.icons.filled.ArrowDropUp
import androidx.compose.material.icons.filled.Security
import androidx.compose.material3.Divider
+import androidx.compose.material3.DropdownMenu
+import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExposedDropdownMenuBox
import androidx.compose.material3.FilterChip
@@ -54,6 +58,9 @@ import me.weishu.kernelsu.ui.component.SwitchItem
import me.weishu.kernelsu.ui.component.profile.AppProfileConfig
import me.weishu.kernelsu.ui.component.profile.RootProfileConfig
import me.weishu.kernelsu.ui.util.LocalSnackbarHost
+import me.weishu.kernelsu.ui.util.forceStopApp
+import me.weishu.kernelsu.ui.util.launchApp
+import me.weishu.kernelsu.ui.util.restartApp
import me.weishu.kernelsu.ui.viewmodel.SuperUserViewModel
/**
@@ -77,10 +84,8 @@ fun AppProfileScreen(
mutableStateOf(Natives.getAppProfile(packageName, appInfo.uid))
}
- Log.i("mylog", "profile: $profile")
-
Scaffold(
- topBar = { TopBar { navigator.popBackStack() } }
+ topBar = { TopBar { navigator.popBackStack() } },
) { paddingValues ->
AppProfileInner(
modifier = Modifier
@@ -128,11 +133,13 @@ private fun AppProfileInner(
val isRootGranted = profile.allowSu
Column(modifier = modifier) {
- ListItem(
- headlineContent = { Text(appLabel) },
- supportingContent = { Text(packageName) },
- leadingContent = appIcon,
- )
+ AppMenuBox(packageName) {
+ ListItem(
+ headlineContent = { Text(appLabel) },
+ supportingContent = { Text(packageName) },
+ leadingContent = appIcon,
+ )
+ }
SwitchItem(
icon = Icons.Filled.Security,
@@ -291,6 +298,51 @@ private fun ProfileBox(
})
}
+@Composable
+private fun AppMenuBox(packageName: String, content: @Composable () -> Unit) {
+
+ var expanded by remember { mutableStateOf(false) }
+ Box {
+
+ Box(modifier = Modifier.clickable {
+ expanded = true
+ }) {
+ content()
+ }
+
+ DropdownMenu(
+ expanded = expanded,
+ onDismissRequest = {
+ expanded = false
+ },
+ ) {
+ DropdownMenuItem(
+ text = { Text(stringResource(id = R.string.launch_app)) },
+ onClick = {
+ expanded = false
+ launchApp(packageName)
+ },
+ )
+ DropdownMenuItem(
+ text = { Text(stringResource(id = R.string.force_stop_app)) },
+ onClick = {
+ expanded = false
+ forceStopApp(packageName)
+ },
+ )
+ DropdownMenuItem(
+ text = { Text(stringResource(id = R.string.restart_app)) },
+ onClick = {
+ expanded = false
+ restartApp(packageName)
+ },
+ )
+ }
+ }
+
+
+}
+
@Preview
@Composable
private fun AppProfilePreview() {
diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/util/KsuCli.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/util/KsuCli.kt
index 1a204fd0..784900aa 100644
--- a/manager/app/src/main/java/me/weishu/kernelsu/ui/util/KsuCli.kt
+++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/util/KsuCli.kt
@@ -9,7 +9,6 @@ import com.topjohnwu.superuser.ShellUtils
import me.weishu.kernelsu.BuildConfig
import me.weishu.kernelsu.Natives
import me.weishu.kernelsu.ksuApp
-import me.weishu.kernelsu.ui.viewmodel.ModuleViewModel
import org.json.JSONArray
import java.io.File
@@ -150,4 +149,22 @@ fun isSepolicyValid(rules: String?): Boolean {
val result =
shell.newJob().add("ksud sepolicy check '$rules'").to(ArrayList(), null).exec()
return result.isSuccess
+}
+
+fun forceStopApp(packageName: String) {
+ val shell = getRootShell()
+ val result = shell.newJob().add("am force-stop $packageName").exec()
+ Log.i(TAG, "force stop $packageName result: $result")
+}
+
+fun launchApp(packageName: String) {
+
+ val shell = getRootShell()
+ val result = shell.newJob().add("monkey -p $packageName -c android.intent.category.LAUNCHER 1").exec()
+ Log.i(TAG, "launch $packageName result: $result")
+}
+
+fun restartApp(packageName: String) {
+ forceStopApp(packageName)
+ launchApp(packageName)
}
\ No newline at end of file
diff --git a/manager/app/src/main/res/values/strings.xml b/manager/app/src/main/res/values/strings.xml
index a8cfc140..410581c0 100644
--- a/manager/app/src/main/res/values/strings.xml
+++ b/manager/app/src/main/res/values/strings.xml
@@ -80,4 +80,7 @@
Downloading module: %s
Start downloading: %s
New version: %s is available, click to download
+ Launch
+ Force Stop
+ Restart