manager: add a simple manager updater, close #627
This commit is contained in:
@@ -19,6 +19,7 @@ import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalUriHandler
|
||||
import androidx.compose.ui.res.stringResource
|
||||
@@ -28,6 +29,8 @@ 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.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import me.weishu.kernelsu.*
|
||||
import me.weishu.kernelsu.R
|
||||
import me.weishu.kernelsu.ui.screen.destinations.SettingScreenDestination
|
||||
@@ -37,13 +40,11 @@ import me.weishu.kernelsu.ui.util.*
|
||||
@Destination
|
||||
@Composable
|
||||
fun HomeScreen(navigator: DestinationsNavigator) {
|
||||
Scaffold(
|
||||
topBar = {
|
||||
Scaffold(topBar = {
|
||||
TopBar(onSettingsClick = {
|
||||
navigator.navigate(SettingScreenDestination)
|
||||
})
|
||||
}
|
||||
) { innerPadding ->
|
||||
}) { innerPadding ->
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(innerPadding)
|
||||
@@ -62,11 +63,11 @@ fun HomeScreen(navigator: DestinationsNavigator) {
|
||||
if (isManager && Natives.requireNewKernel()) {
|
||||
WarningCard(
|
||||
stringResource(id = R.string.require_kernel_version).format(
|
||||
ksuVersion,
|
||||
Natives.MINIMAL_SUPPORTED_KERNEL
|
||||
ksuVersion, Natives.MINIMAL_SUPPORTED_KERNEL
|
||||
)
|
||||
)
|
||||
}
|
||||
UpdateCard()
|
||||
InfoCard()
|
||||
DonateCard()
|
||||
LearnMoreCard()
|
||||
@@ -75,6 +76,25 @@ fun HomeScreen(navigator: DestinationsNavigator) {
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun UpdateCard() {
|
||||
val context = LocalContext.current
|
||||
val newVersion by produceState(initialValue = 0 to "") {
|
||||
value = withContext(Dispatchers.IO) { checkNewVersion() }
|
||||
}
|
||||
val currentVersionCode = getManagerVersion(context).second
|
||||
val newVersionCode = newVersion.first
|
||||
val newVersionUrl = newVersion.second
|
||||
if (newVersionCode <= currentVersionCode) {
|
||||
return
|
||||
}
|
||||
|
||||
val uriHandler = LocalUriHandler.current
|
||||
WarningCard(message = stringResource(id = R.string.new_version_available).format(newVersionCode)) {
|
||||
uriHandler.openUri(newVersionUrl)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun RebootDropdownItem(@StringRes id: Int, reason: String = "") {
|
||||
DropdownMenuItem(text = {
|
||||
@@ -87,9 +107,7 @@ fun RebootDropdownItem(@StringRes id: Int, reason: String = "") {
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
private fun TopBar(onSettingsClick: () -> Unit) {
|
||||
TopAppBar(
|
||||
title = { Text(stringResource(R.string.app_name)) },
|
||||
actions = {
|
||||
TopAppBar(title = { Text(stringResource(R.string.app_name)) }, actions = {
|
||||
var showDropdown by remember { mutableStateOf(false) }
|
||||
IconButton(onClick = {
|
||||
showDropdown = true
|
||||
@@ -123,8 +141,7 @@ private fun TopBar(onSettingsClick: () -> Unit) {
|
||||
contentDescription = stringResource(id = R.string.settings)
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
@Composable
|
||||
@@ -136,17 +153,14 @@ private fun StatusCard(kernelVersion: KernelVersion, ksuVersion: Int?) {
|
||||
})
|
||||
) {
|
||||
val uriHandler = LocalUriHandler.current
|
||||
Row(
|
||||
modifier = Modifier
|
||||
Row(modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable {
|
||||
if (kernelVersion.isGKI() && ksuVersion == null) {
|
||||
uriHandler.openUri("https://kernelsu.org/guide/installation.html")
|
||||
}
|
||||
}
|
||||
.padding(24.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
.padding(24.dp), verticalAlignment = Alignment.CenterVertically) {
|
||||
when {
|
||||
ksuVersion != null -> {
|
||||
val appendText = if (Natives.isSafeMode) {
|
||||
@@ -168,10 +182,8 @@ private fun StatusCard(kernelVersion: KernelVersion, ksuVersion: Int?) {
|
||||
Spacer(Modifier.height(4.dp))
|
||||
Text(
|
||||
text = stringResource(
|
||||
R.string.home_superuser_count,
|
||||
getSuperuserCount()
|
||||
),
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
R.string.home_superuser_count, getSuperuserCount()
|
||||
), style = MaterialTheme.typography.bodyMedium
|
||||
)
|
||||
Spacer(Modifier.height(4.dp))
|
||||
Text(
|
||||
@@ -217,22 +229,25 @@ private fun StatusCard(kernelVersion: KernelVersion, ksuVersion: Int?) {
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun WarningCard(message: String) {
|
||||
fun WarningCard(
|
||||
message: String, color: Color = MaterialTheme.colorScheme.error, onClick: () -> Unit = {}
|
||||
) {
|
||||
ElevatedCard(
|
||||
colors = CardDefaults.elevatedCardColors(
|
||||
containerColor = MaterialTheme.colorScheme.error
|
||||
containerColor = color
|
||||
)
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(24.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
.padding(24.dp)
|
||||
.clickable {
|
||||
onClick()
|
||||
}, verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Column() {
|
||||
Text(
|
||||
text = message,
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
text = message, style = MaterialTheme.typography.bodyMedium
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -246,15 +261,12 @@ fun LearnMoreCard() {
|
||||
|
||||
ElevatedCard {
|
||||
|
||||
Row(
|
||||
modifier = Modifier
|
||||
Row(modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable {
|
||||
uriHandler.openUri(url)
|
||||
}
|
||||
.padding(24.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
.padding(24.dp), verticalAlignment = Alignment.CenterVertically) {
|
||||
Column() {
|
||||
Text(
|
||||
text = stringResource(R.string.home_learn_kernelsu),
|
||||
@@ -276,15 +288,12 @@ fun DonateCard() {
|
||||
|
||||
ElevatedCard {
|
||||
|
||||
Row(
|
||||
modifier = Modifier
|
||||
Row(modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable {
|
||||
uriHandler.openUri("https://patreon.com/weishu")
|
||||
}
|
||||
.padding(24.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
.padding(24.dp), verticalAlignment = Alignment.CenterVertically) {
|
||||
Column() {
|
||||
Text(
|
||||
text = stringResource(R.string.home_support_title),
|
||||
@@ -323,7 +332,11 @@ private fun InfoCard() {
|
||||
InfoCardItem(stringResource(R.string.home_kernel), uname.release)
|
||||
|
||||
Spacer(Modifier.height(16.dp))
|
||||
InfoCardItem(stringResource(R.string.home_manager_version), getManagerVersion(context))
|
||||
val managerVersion = getManagerVersion(context)
|
||||
InfoCardItem(
|
||||
stringResource(R.string.home_manager_version),
|
||||
"${managerVersion.first} (${managerVersion.second})"
|
||||
)
|
||||
|
||||
Spacer(Modifier.height(16.dp))
|
||||
InfoCardItem(stringResource(R.string.home_fingerprint), Build.FINGERPRINT)
|
||||
@@ -334,9 +347,9 @@ private fun InfoCard() {
|
||||
}
|
||||
}
|
||||
|
||||
fun getManagerVersion(context: Context): String {
|
||||
fun getManagerVersion(context: Context): Pair<String, Int> {
|
||||
val packageInfo = context.packageManager.getPackageInfo(context.packageName, 0)
|
||||
return "${packageInfo.versionName} (${packageInfo.versionCode})"
|
||||
return Pair(packageInfo.versionName, packageInfo.versionCode)
|
||||
}
|
||||
|
||||
@Preview
|
||||
|
||||
@@ -60,6 +60,40 @@ fun download(
|
||||
downloadManager.enqueue(request)
|
||||
}
|
||||
|
||||
fun checkNewVersion(): Pair<Int, String> {
|
||||
val url = "https://api.github.com/repos/tiann/KernelSU/releases/latest"
|
||||
val defaultValue = 0 to ""
|
||||
runCatching {
|
||||
okhttp3.OkHttpClient().newCall(okhttp3.Request.Builder().url(url).build()).execute()
|
||||
.use { response ->
|
||||
if (!response.isSuccessful) {
|
||||
return defaultValue
|
||||
}
|
||||
val body = response.body?.string() ?: return defaultValue
|
||||
val json = org.json.JSONObject(body)
|
||||
|
||||
val assets = json.getJSONArray("assets")
|
||||
for (i in 0 until assets.length()) {
|
||||
val asset = assets.getJSONObject(i)
|
||||
val name = asset.getString("name")
|
||||
if (!name.endsWith(".apk")) {
|
||||
continue
|
||||
}
|
||||
|
||||
val regex = Regex("v(.+?)_(\\d+)-")
|
||||
val matchResult = regex.find(name) ?: continue
|
||||
val versionName = matchResult.groupValues[1]
|
||||
val versionCode = matchResult.groupValues[2].toInt()
|
||||
val downloadUrl = asset.getString("browser_download_url")
|
||||
|
||||
return versionCode to downloadUrl
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun DownloadListener(context: Context, onDownloaded: (Uri) -> Unit) {
|
||||
DisposableEffect(context) {
|
||||
|
||||
@@ -74,4 +74,5 @@
|
||||
<string name="module_update">更新</string>
|
||||
<string name="module_downloading">正在下载模块:%s</string>
|
||||
<string name="module_start_downloading">开始下载:%s</string>
|
||||
<string name="new_version_available">发现新版本:%d,点击下载</string>
|
||||
</resources>
|
||||
|
||||
@@ -79,4 +79,5 @@
|
||||
<string name="module_update">Update</string>
|
||||
<string name="module_downloading">Downloading module: %s</string>
|
||||
<string name="module_start_downloading">Start downloading: %s</string>
|
||||
<string name="new_version_available">New version: %s is available, click to download</string>
|
||||
</resources>
|
||||
|
||||
Reference in New Issue
Block a user