manager: add a simple manager updater, close #627

This commit is contained in:
weishu
2023-06-23 00:31:36 +08:00
parent 12761ee167
commit 37f4045499
4 changed files with 131 additions and 82 deletions

View File

@@ -19,6 +19,7 @@ import androidx.compose.material3.*
import androidx.compose.runtime.* import androidx.compose.runtime.*
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalUriHandler import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.res.stringResource 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.Destination
import com.ramcosta.composedestinations.annotation.RootNavGraph import com.ramcosta.composedestinations.annotation.RootNavGraph
import com.ramcosta.composedestinations.navigation.DestinationsNavigator import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import me.weishu.kernelsu.* import me.weishu.kernelsu.*
import me.weishu.kernelsu.R import me.weishu.kernelsu.R
import me.weishu.kernelsu.ui.screen.destinations.SettingScreenDestination import me.weishu.kernelsu.ui.screen.destinations.SettingScreenDestination
@@ -37,13 +40,11 @@ import me.weishu.kernelsu.ui.util.*
@Destination @Destination
@Composable @Composable
fun HomeScreen(navigator: DestinationsNavigator) { fun HomeScreen(navigator: DestinationsNavigator) {
Scaffold( Scaffold(topBar = {
topBar = { TopBar(onSettingsClick = {
TopBar(onSettingsClick = { navigator.navigate(SettingScreenDestination)
navigator.navigate(SettingScreenDestination) })
}) }) { innerPadding ->
}
) { innerPadding ->
Column( Column(
modifier = Modifier modifier = Modifier
.padding(innerPadding) .padding(innerPadding)
@@ -62,11 +63,11 @@ fun HomeScreen(navigator: DestinationsNavigator) {
if (isManager && Natives.requireNewKernel()) { if (isManager && Natives.requireNewKernel()) {
WarningCard( WarningCard(
stringResource(id = R.string.require_kernel_version).format( stringResource(id = R.string.require_kernel_version).format(
ksuVersion, ksuVersion, Natives.MINIMAL_SUPPORTED_KERNEL
Natives.MINIMAL_SUPPORTED_KERNEL
) )
) )
} }
UpdateCard()
InfoCard() InfoCard()
DonateCard() DonateCard()
LearnMoreCard() 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 @Composable
fun RebootDropdownItem(@StringRes id: Int, reason: String = "") { fun RebootDropdownItem(@StringRes id: Int, reason: String = "") {
DropdownMenuItem(text = { DropdownMenuItem(text = {
@@ -87,44 +107,41 @@ fun RebootDropdownItem(@StringRes id: Int, reason: String = "") {
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
private fun TopBar(onSettingsClick: () -> Unit) { private fun TopBar(onSettingsClick: () -> Unit) {
TopAppBar( TopAppBar(title = { Text(stringResource(R.string.app_name)) }, actions = {
title = { Text(stringResource(R.string.app_name)) }, var showDropdown by remember { mutableStateOf(false) }
actions = { IconButton(onClick = {
var showDropdown by remember { mutableStateOf(false) } showDropdown = true
IconButton(onClick = { }) {
showDropdown = true Icon(
imageVector = Icons.Filled.Refresh,
contentDescription = stringResource(id = R.string.reboot)
)
DropdownMenu(expanded = showDropdown, onDismissRequest = {
showDropdown = false
}) { }) {
Icon(
imageVector = Icons.Filled.Refresh,
contentDescription = stringResource(id = R.string.reboot)
)
DropdownMenu(expanded = showDropdown, onDismissRequest = { RebootDropdownItem(id = R.string.reboot)
showDropdown = false
}) {
RebootDropdownItem(id = R.string.reboot) val pm =
LocalContext.current.getSystemService(Context.POWER_SERVICE) as PowerManager?
val pm = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && pm?.isRebootingUserspaceSupported == true) {
LocalContext.current.getSystemService(Context.POWER_SERVICE) as PowerManager? RebootDropdownItem(id = R.string.reboot_userspace, reason = "userspace")
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && pm?.isRebootingUserspaceSupported == true) {
RebootDropdownItem(id = R.string.reboot_userspace, reason = "userspace")
}
RebootDropdownItem(id = R.string.reboot_recovery, reason = "recovery")
RebootDropdownItem(id = R.string.reboot_bootloader, reason = "bootloader")
RebootDropdownItem(id = R.string.reboot_download, reason = "download")
RebootDropdownItem(id = R.string.reboot_edl, reason = "edl")
} }
} RebootDropdownItem(id = R.string.reboot_recovery, reason = "recovery")
RebootDropdownItem(id = R.string.reboot_bootloader, reason = "bootloader")
IconButton(onClick = onSettingsClick) { RebootDropdownItem(id = R.string.reboot_download, reason = "download")
Icon( RebootDropdownItem(id = R.string.reboot_edl, reason = "edl")
imageVector = Icons.Filled.Settings,
contentDescription = stringResource(id = R.string.settings)
)
} }
} }
)
IconButton(onClick = onSettingsClick) {
Icon(
imageVector = Icons.Filled.Settings,
contentDescription = stringResource(id = R.string.settings)
)
}
})
} }
@Composable @Composable
@@ -136,17 +153,14 @@ private fun StatusCard(kernelVersion: KernelVersion, ksuVersion: Int?) {
}) })
) { ) {
val uriHandler = LocalUriHandler.current val uriHandler = LocalUriHandler.current
Row( Row(modifier = Modifier
modifier = Modifier .fillMaxWidth()
.fillMaxWidth() .clickable {
.clickable { if (kernelVersion.isGKI() && ksuVersion == null) {
if (kernelVersion.isGKI() && ksuVersion == null) { uriHandler.openUri("https://kernelsu.org/guide/installation.html")
uriHandler.openUri("https://kernelsu.org/guide/installation.html")
}
} }
.padding(24.dp), }
verticalAlignment = Alignment.CenterVertically .padding(24.dp), verticalAlignment = Alignment.CenterVertically) {
) {
when { when {
ksuVersion != null -> { ksuVersion != null -> {
val appendText = if (Natives.isSafeMode) { val appendText = if (Natives.isSafeMode) {
@@ -168,10 +182,8 @@ private fun StatusCard(kernelVersion: KernelVersion, ksuVersion: Int?) {
Spacer(Modifier.height(4.dp)) Spacer(Modifier.height(4.dp))
Text( Text(
text = stringResource( text = stringResource(
R.string.home_superuser_count, R.string.home_superuser_count, getSuperuserCount()
getSuperuserCount() ), style = MaterialTheme.typography.bodyMedium
),
style = MaterialTheme.typography.bodyMedium
) )
Spacer(Modifier.height(4.dp)) Spacer(Modifier.height(4.dp))
Text( Text(
@@ -217,22 +229,25 @@ private fun StatusCard(kernelVersion: KernelVersion, ksuVersion: Int?) {
} }
@Composable @Composable
fun WarningCard(message: String) { fun WarningCard(
message: String, color: Color = MaterialTheme.colorScheme.error, onClick: () -> Unit = {}
) {
ElevatedCard( ElevatedCard(
colors = CardDefaults.elevatedCardColors( colors = CardDefaults.elevatedCardColors(
containerColor = MaterialTheme.colorScheme.error containerColor = color
) )
) { ) {
Row( Row(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(24.dp), .padding(24.dp)
verticalAlignment = Alignment.CenterVertically .clickable {
onClick()
}, verticalAlignment = Alignment.CenterVertically
) { ) {
Column() { Column() {
Text( Text(
text = message, text = message, style = MaterialTheme.typography.bodyMedium
style = MaterialTheme.typography.bodyMedium
) )
} }
} }
@@ -246,15 +261,12 @@ fun LearnMoreCard() {
ElevatedCard { ElevatedCard {
Row( Row(modifier = Modifier
modifier = Modifier .fillMaxWidth()
.fillMaxWidth() .clickable {
.clickable { uriHandler.openUri(url)
uriHandler.openUri(url) }
} .padding(24.dp), verticalAlignment = Alignment.CenterVertically) {
.padding(24.dp),
verticalAlignment = Alignment.CenterVertically
) {
Column() { Column() {
Text( Text(
text = stringResource(R.string.home_learn_kernelsu), text = stringResource(R.string.home_learn_kernelsu),
@@ -276,15 +288,12 @@ fun DonateCard() {
ElevatedCard { ElevatedCard {
Row( Row(modifier = Modifier
modifier = Modifier .fillMaxWidth()
.fillMaxWidth() .clickable {
.clickable { uriHandler.openUri("https://patreon.com/weishu")
uriHandler.openUri("https://patreon.com/weishu") }
} .padding(24.dp), verticalAlignment = Alignment.CenterVertically) {
.padding(24.dp),
verticalAlignment = Alignment.CenterVertically
) {
Column() { Column() {
Text( Text(
text = stringResource(R.string.home_support_title), text = stringResource(R.string.home_support_title),
@@ -323,7 +332,11 @@ private fun InfoCard() {
InfoCardItem(stringResource(R.string.home_kernel), uname.release) InfoCardItem(stringResource(R.string.home_kernel), uname.release)
Spacer(Modifier.height(16.dp)) 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)) Spacer(Modifier.height(16.dp))
InfoCardItem(stringResource(R.string.home_fingerprint), Build.FINGERPRINT) 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) val packageInfo = context.packageManager.getPackageInfo(context.packageName, 0)
return "${packageInfo.versionName} (${packageInfo.versionCode})" return Pair(packageInfo.versionName, packageInfo.versionCode)
} }
@Preview @Preview

View File

@@ -60,6 +60,40 @@ fun download(
downloadManager.enqueue(request) 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 @Composable
fun DownloadListener(context: Context, onDownloaded: (Uri) -> Unit) { fun DownloadListener(context: Context, onDownloaded: (Uri) -> Unit) {
DisposableEffect(context) { DisposableEffect(context) {

View File

@@ -74,4 +74,5 @@
<string name="module_update">更新</string> <string name="module_update">更新</string>
<string name="module_downloading">正在下载模块:%s</string> <string name="module_downloading">正在下载模块:%s</string>
<string name="module_start_downloading">开始下载:%s</string> <string name="module_start_downloading">开始下载:%s</string>
<string name="new_version_available">发现新版本:%d点击下载</string>
</resources> </resources>

View File

@@ -79,4 +79,5 @@
<string name="module_update">Update</string> <string name="module_update">Update</string>
<string name="module_downloading">Downloading module: %s</string> <string name="module_downloading">Downloading module: %s</string>
<string name="module_start_downloading">Start downloading: %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> </resources>