diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Home.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Home.kt
index d8059668..87833160 100644
--- a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Home.kt
+++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Home.kt
@@ -1,7 +1,5 @@
package me.weishu.kernelsu.ui.screen
-import android.content.ClipData
-import android.content.ClipboardManager
import android.content.Context
import android.os.Build
import android.os.PowerManager
@@ -30,14 +28,10 @@ import androidx.compose.ui.unit.dp
import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.annotation.RootNavGraph
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
-import kotlinx.coroutines.launch
import me.weishu.kernelsu.*
import me.weishu.kernelsu.R
import me.weishu.kernelsu.ui.screen.destinations.SettingScreenDestination
-import me.weishu.kernelsu.ui.util.LocalSnackbarHost
-import me.weishu.kernelsu.ui.util.reboot
-import me.weishu.kernelsu.ui.util.getSELinuxStatus
-import me.weishu.kernelsu.ui.util.install
+import me.weishu.kernelsu.ui.util.*
@OptIn(ExperimentalMaterial3Api::class)
@RootNavGraph(start = true)
@@ -67,6 +61,8 @@ fun HomeScreen(navigator: DestinationsNavigator) {
StatusCard(kernelVersion, ksuVersion)
InfoCard()
+ DonateCard()
+ AboutCard()
Spacer(Modifier)
}
}
@@ -102,7 +98,8 @@ private fun TopBar(onSettingsClick: () -> Unit) {
RebootDropdownItem(id = R.string.reboot)
- val pm = LocalContext.current.getSystemService(Context.POWER_SERVICE) as PowerManager?
+ val pm =
+ LocalContext.current.getSystemService(Context.POWER_SERVICE) as PowerManager?
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && pm?.isRebootingUserspaceSupported == true) {
RebootDropdownItem(id = R.string.reboot_userspace, reason = "userspace")
}
@@ -161,6 +158,16 @@ private fun StatusCard(kernelVersion: KernelVersion, ksuVersion: Int?) {
text = stringResource(R.string.home_working_version, ksuVersion),
style = MaterialTheme.typography.bodyMedium
)
+ Spacer(Modifier.height(4.dp))
+ Text(
+ text = stringResource(R.string.home_superuser_count, getSuperuserCount()),
+ style = MaterialTheme.typography.bodyMedium
+ )
+ Spacer(Modifier.height(4.dp))
+ Text(
+ text = stringResource(R.string.home_module_count, getModuleCount()),
+ style = MaterialTheme.typography.bodyMedium
+ )
}
}
kernelVersion.isGKI() -> {
@@ -197,11 +204,69 @@ private fun StatusCard(kernelVersion: KernelVersion, ksuVersion: Int?) {
}
}
+@Composable
+fun AboutCard() {
+ val uriHandler = LocalUriHandler.current
+
+ ElevatedCard {
+
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .clickable {
+ uriHandler.openUri("https://kernelsu.org/guide/what-is-kernelsu.html")
+ }
+ .padding(24.dp),
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Column() {
+ Text(
+ text = stringResource(R.string.home_learn_kernelsu),
+ style = MaterialTheme.typography.titleSmall
+ )
+ Spacer(Modifier.height(4.dp))
+ Text(
+ text = stringResource(R.string.home_click_to_learn_kernelsu),
+ style = MaterialTheme.typography.bodyMedium
+ )
+ }
+ }
+ }
+}
+
+@Composable
+fun DonateCard() {
+ val uriHandler = LocalUriHandler.current
+
+ ElevatedCard {
+
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .clickable {
+ uriHandler.openUri("https://patreon.com/weishu")
+ }
+ .padding(24.dp),
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Column() {
+ Text(
+ text = stringResource(R.string.home_support_title),
+ style = MaterialTheme.typography.titleSmall
+ )
+ Spacer(Modifier.height(4.dp))
+ Text(
+ text = stringResource(R.string.home_support_content),
+ style = MaterialTheme.typography.bodyMedium
+ )
+ }
+ }
+ }
+}
+
@Composable
private fun InfoCard() {
val context = LocalContext.current
- val snackbarHost = LocalSnackbarHost.current
- val scope = rememberCoroutineScope()
ElevatedCard {
Column(
@@ -221,39 +286,19 @@ private fun InfoCard() {
InfoCardItem(stringResource(R.string.home_kernel), uname.release)
- Spacer(Modifier.height(24.dp))
+ Spacer(Modifier.height(16.dp))
InfoCardItem(stringResource(R.string.home_manager_version), getManagerVersion(context))
- Spacer(Modifier.height(24.dp))
- InfoCardItem(stringResource(R.string.home_api), Build.VERSION.SDK_INT.toString())
-
- Spacer(Modifier.height(24.dp))
- InfoCardItem(stringResource(R.string.home_abi), Build.SUPPORTED_ABIS.joinToString(", "))
-
- Spacer(Modifier.height(24.dp))
+ Spacer(Modifier.height(16.dp))
InfoCardItem(stringResource(R.string.home_fingerprint), Build.FINGERPRINT)
- Spacer(Modifier.height(24.dp))
- InfoCardItem(stringResource(R.string.home_securitypatch), Build.VERSION.SECURITY_PATCH)
-
- Spacer(Modifier.height(24.dp))
+ Spacer(Modifier.height(16.dp))
InfoCardItem(stringResource(R.string.home_selinux_status), getSELinuxStatus())
-
- val copiedMessage = stringResource(R.string.home_copied_to_clipboard)
- TextButton(
- modifier = Modifier.align(Alignment.End),
- onClick = {
- val cm = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
- cm.setPrimaryClip(ClipData.newPlainText("KernelSU", contents.toString()))
- scope.launch { snackbarHost.showSnackbar(copiedMessage) }
- },
- content = { Text(stringResource(android.R.string.copy)) }
- )
}
}
}
-fun getManagerVersion(context: Context) : String {
+fun getManagerVersion(context: Context): String {
val packageInfo = context.packageManager.getPackageInfo(context.packageName, 0)
return "${packageInfo.versionName} (${packageInfo.versionCode})"
}
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 bb17a037..4cf9bc0b 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
@@ -7,7 +7,10 @@ import com.topjohnwu.superuser.CallbackList
import com.topjohnwu.superuser.Shell
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
@@ -54,10 +57,23 @@ fun install() {
fun listModules(): String {
val shell = getRootShell()
- val out = shell.newJob().add("${getKsuDaemonPath()} module list").to(ArrayList(), null).exec().out
+ val out =
+ shell.newJob().add("${getKsuDaemonPath()} module list").to(ArrayList(), null).exec().out
return out.joinToString("\n").ifBlank { "[]" }
}
+fun getModuleCount(): Int {
+ val result = listModules()
+ runCatching {
+ val array = JSONArray(result)
+ return array.length()
+ }.getOrElse { return 0 }
+}
+
+fun getSuperuserCount(): Int {
+ return Natives.getAllowList().size
+}
+
fun toggleModule(id: String, enable: Boolean): Boolean {
val cmd = if (enable) {
"module enable $id"
@@ -69,14 +85,14 @@ fun toggleModule(id: String, enable: Boolean): Boolean {
return result
}
-fun uninstallModule(id: String) : Boolean {
+fun uninstallModule(id: String): Boolean {
val cmd = "module uninstall $id"
val result = execKsud(cmd)
Log.i(TAG, "uninstall module $id result: $result")
return result
}
-fun installModule(uri: Uri, onFinish: (Boolean)->Unit, onOutput: (String) -> Unit) : Boolean {
+fun installModule(uri: Uri, onFinish: (Boolean) -> Unit, onOutput: (String) -> Unit): Boolean {
val resolver = ksuApp.contentResolver
with(resolver.openInputStream(uri)) {
val file = File(ksuApp.cacheDir, "module.zip")
@@ -93,7 +109,8 @@ fun installModule(uri: Uri, onFinish: (Boolean)->Unit, onOutput: (String) -> Uni
}
}
- val result = shell.newJob().add("${getKsuDaemonPath()} $cmd").to(callbackList, callbackList).exec()
+ val result =
+ shell.newJob().add("${getKsuDaemonPath()} $cmd").to(callbackList, callbackList).exec()
Log.i("KernelSU", "install module $uri result: $result")
file.delete()
diff --git a/manager/app/src/main/res/values-ja/strings.xml b/manager/app/src/main/res/values-ja/strings.xml
index bfe1c1d2..e6d0abc7 100644
--- a/manager/app/src/main/res/values-ja/strings.xml
+++ b/manager/app/src/main/res/values-ja/strings.xml
@@ -1,7 +1,6 @@
- KernelSU
- ホーム
+ ホーム
未インストール
タップでインストール
動作中
@@ -40,6 +39,7 @@
EDLで再起動
アプリについて
KernelSU バージョン8以降が必要です
+ モジュール %s をアンインストールしますか?
%sをアンインストールしました
アンインストールに失敗: %s
バージョン
diff --git a/manager/app/src/main/res/values-zh-rCN/strings.xml b/manager/app/src/main/res/values-zh-rCN/strings.xml
index 4db2282e..c12c9de8 100644
--- a/manager/app/src/main/res/values-zh-rCN/strings.xml
+++ b/manager/app/src/main/res/values-zh-rCN/strings.xml
@@ -5,6 +5,7 @@
点击安装
工作中
版本: %d
+ 超级用户数:%d
不支持
KernelSU 现在只支持 GKI 内核
已复制到剪贴板
@@ -53,4 +54,9 @@
安全模式
重启生效
所有模块已被禁用,因为它与 Magisk 的模块系统有冲突!
+ 模块数:%d
+ 了解 KernelSU
+ 了解如何安装 KernelSU 以及如何开发模块
+ 支持开发
+ KernelSU 将保持免费和开源,向开发者捐赠以表示支持。
diff --git a/manager/app/src/main/res/values/strings.xml b/manager/app/src/main/res/values/strings.xml
index ac4c4f8d..3e652f4f 100644
--- a/manager/app/src/main/res/values/strings.xml
+++ b/manager/app/src/main/res/values/strings.xml
@@ -6,6 +6,8 @@
Click to install
Working
Version: %d
+ Superusers: %d
+ Modules: %d
Unsupported
KernelSU only supports GKI kernels now
Copied to clipboard
@@ -57,5 +59,8 @@
Safe mode
Reboot to take effect
Modules are disabled because it is conflict with Magisk\'s!
-
+ Learn KernelSU
+ Learn how to install KernelSU and use modules
+ Support Us
+ KernelSU is, and always will be, free, and open source. You can however show us that you care by making a donation.