feat: update channel
Some checks are pending
Build-Apk / build (push) Waiting to run

This commit is contained in:
lisonge 2024-08-17 20:00:21 +08:00
parent 57d92e4ce1
commit 8e3246a9a6
7 changed files with 105 additions and 34 deletions

View File

@ -10,7 +10,6 @@ import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import li.songe.gkd.BuildConfig.ENABLED_UPDATE
import li.songe.gkd.data.RawSubscription
import li.songe.gkd.data.SubsItem
import li.songe.gkd.db.DbSet
@ -67,7 +66,7 @@ class MainViewModel : ViewModel() {
clearCache()
}
if (ENABLED_UPDATE && storeFlow.value.autoCheckAppUpdate) {
if (BuildConfig.ENABLED_UPDATE && storeFlow.value.autoCheckAppUpdate) {
viewModelScope.launch(Dispatchers.IO) {
try {
updateStatus.checkUpdate()

View File

@ -39,12 +39,13 @@ import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.viewModelScope
import com.ramcosta.composedestinations.navigation.navigate
import kotlinx.coroutines.flow.update
import li.songe.gkd.BuildConfig.ENABLED_UPDATE
import li.songe.gkd.BuildConfig
import li.songe.gkd.ui.component.RotatingLoadingIcon
import li.songe.gkd.ui.component.SettingItem
import li.songe.gkd.ui.component.TextMenu
import li.songe.gkd.ui.component.TextSwitch
import li.songe.gkd.ui.component.updateDialogOptions
import li.songe.gkd.ui.component.waitResult
import li.songe.gkd.ui.destinations.AboutPageDestination
import li.songe.gkd.ui.destinations.AdvancedPageDestination
import li.songe.gkd.ui.style.EmptyHeight
@ -54,10 +55,12 @@ import li.songe.gkd.ui.theme.supportDynamicColor
import li.songe.gkd.util.DarkThemeOption
import li.songe.gkd.util.LocalMainViewModel
import li.songe.gkd.util.LocalNavController
import li.songe.gkd.util.UpdateChannelOption
import li.songe.gkd.util.UpdateTimeOption
import li.songe.gkd.util.checkUpdate
import li.songe.gkd.util.findOption
import li.songe.gkd.util.launchAsFn
import li.songe.gkd.util.launchTry
import li.songe.gkd.util.storeFlow
import li.songe.gkd.util.throttle
import li.songe.gkd.util.toast
@ -324,15 +327,34 @@ fun useSettingsPage(): ScaffoldExt {
storeFlow.update { s -> s.copy(updateSubsInterval = it.value) }
}
if (ENABLED_UPDATE) {
TextSwitch(name = "自动更新",
if (BuildConfig.ENABLED_UPDATE) {
TextSwitch(
name = "自动更新",
desc = "打开应用时检测新版本",
checked = store.autoCheckAppUpdate,
onCheckedChange = {
storeFlow.value = store.copy(
autoCheckAppUpdate = it
)
})
}
)
TextMenu(
title = "更新渠道",
option = UpdateChannelOption.allSubObject.findOption(store.updateChannel)
) {
if (it.value == UpdateChannelOption.Beta.value) {
vm.viewModelScope.launchTry {
mainVm.dialogFlow.waitResult(
title = "版本渠道",
text = "测试版本渠道更新快\n但不稳定可能存在较多BUG\n请谨慎使用",
)
storeFlow.update { s -> s.copy(updateChannel = it.value) }
}
} else {
storeFlow.update { s -> s.copy(updateChannel = it.value) }
}
}
Row(
modifier = Modifier

View File

@ -9,8 +9,6 @@ const val FILE_UPLOAD_URL = "https://u.gkd.li/"
const val FILE_SHORT_URL = "https://f.gkd.li/"
const val IMPORT_BASE_URL = "https://i.gkd.li/i/"
const val UPDATE_URL = "https://registry.npmmirror.com/@gkd-kit/app/latest/files/index.json"
const val SERVER_SCRIPT_URL =
"https://registry-direct.npmmirror.com/@gkd-kit/config/latest/files/dist/server.js"

View File

@ -17,6 +17,7 @@ val <T> Option<T>.allSubObject: Array<Option<T>>
is DarkThemeOption -> DarkThemeOption.allSubObject
is EnableGroupOption -> EnableGroupOption.allSubObject
is RuleSortOption -> RuleSortOption.allSubObject
is UpdateChannelOption -> UpdateChannelOption.allSubObject
} as Array<Option<T>>
sealed class SortTypeOption(override val value: Int, override val label: String) : Option<Int> {
@ -72,3 +73,20 @@ sealed class RuleSortOption(override val value: Int, override val label: String)
val allSubObject by lazy { arrayOf(Default, ByTime, ByName) }
}
}
sealed class UpdateChannelOption(override val value: Int, override val label: String) :
Option<Int> {
abstract val url: String
data object Stable : UpdateChannelOption(0, "稳定版") {
override val url = "https://registry.npmmirror.com/@gkd-kit/app/latest/files/index.json"
}
data object Beta : UpdateChannelOption(1, "测试版") {
override val url = "https://registry.npmmirror.com/@gkd-kit/app-beta/latest/files/index.json"
}
companion object {
val allSubObject by lazy { arrayOf(Stable, Beta) }
}
}

View File

@ -9,7 +9,7 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlinx.serialization.Serializable
import kotlinx.serialization.encodeToString
import li.songe.gkd.BuildConfig.ENABLED_UPDATE
import li.songe.gkd.BuildConfig
import li.songe.gkd.appScope
private inline fun <reified T> createStorageFlow(
@ -48,7 +48,7 @@ data class Store(
val httpServerPort: Int = 8888,
val updateSubsInterval: Long = UpdateTimeOption.Everyday.value,
val captureVolumeChange: Boolean = false,
val autoCheckAppUpdate: Boolean = ENABLED_UPDATE,
val autoCheckAppUpdate: Boolean = BuildConfig.ENABLED_UPDATE,
val toastWhenClick: Boolean = true,
val clickToast: String = "GKD",
val autoClearMemorySubs: Boolean = true,
@ -67,6 +67,7 @@ data class Store(
val useCustomNotifText: Boolean = false,
val customNotifText: String = "\${i}全局/\${k}应用/\${u}规则组/\${n}触发",
val enableActivityLog: Boolean = false,
val updateChannel: Int = if (BuildConfig.VERSION_NAME.contains("beta")) UpdateChannelOption.Beta.value else UpdateChannelOption.Stable.value,
)
val storeFlow by lazy {

View File

@ -1,5 +1,6 @@
package li.songe.gkd.util
import android.content.Intent
import android.util.Log
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.heightIn
@ -14,8 +15,8 @@ import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.core.content.FileProvider
import androidx.lifecycle.viewModelScope
import com.blankj.utilcode.util.AppUtils
import com.blankj.utilcode.util.NetworkUtils
import io.ktor.client.call.body
import io.ktor.client.plugins.onDownload
@ -31,6 +32,7 @@ import kotlinx.coroutines.withContext
import kotlinx.serialization.Serializable
import li.songe.gkd.BuildConfig
import li.songe.gkd.MainViewModel
import li.songe.gkd.app
import java.io.File
import java.net.URI
@ -54,9 +56,12 @@ data class VersionLog(
class UpdateStatus {
val checkUpdatingFlow = MutableStateFlow(false)
val newVersionFlow = MutableStateFlow<NewVersion?>(null)
val downloadStatusFlow = MutableStateFlow<LoadStatus<String>?>(null)
val downloadStatusFlow = MutableStateFlow<LoadStatus<File>?>(null)
}
private val UPDATE_URL: String
get() = UpdateChannelOption.allSubObject.findOption(storeFlow.value.updateChannel).url
suspend fun UpdateStatus.checkUpdate(): NewVersion? {
if (checkUpdatingFlow.value) return null
val isAvailable = withContext(Dispatchers.IO) { NetworkUtils.isAvailable() }
@ -82,7 +87,7 @@ suspend fun UpdateStatus.checkUpdate(): NewVersion? {
private fun UpdateStatus.startDownload(viewModel: MainViewModel, newVersion: NewVersion) {
if (downloadStatusFlow.value is LoadStatus.Loading) return
downloadStatusFlow.value = LoadStatus.Loading(0f)
val newApkFile = File(newVersionApkDir, "v${newVersion.versionCode}.apk")
val newApkFile = newVersionApkDir.resolve("v${newVersion.versionCode}.apk")
if (newApkFile.exists()) {
newApkFile.delete()
}
@ -105,7 +110,7 @@ private fun UpdateStatus.startDownload(viewModel: MainViewModel, newVersion: New
}.bodyAsChannel()
if (downloadStatusFlow.value is LoadStatus.Loading) {
channel.copyAndClose(newApkFile.writeChannel())
downloadStatusFlow.value = LoadStatus.Success(newApkFile.absolutePath)
downloadStatusFlow.value = LoadStatus.Success(newApkFile)
}
} catch (e: Exception) {
if (downloadStatusFlow.value is LoadStatus.Loading) {
@ -121,7 +126,7 @@ fun UpgradeDialog(status: UpdateStatus) {
val newVersion by status.newVersionFlow.collectAsState()
newVersion?.let { newVersionVal ->
AlertDialog(title = {
Text(text = "检测到新版本")
Text(text = "新版本")
}, text = {
Text(text = "v${BuildConfig.VERSION_NAME} -> v${newVersionVal.versionName}\n\n${
if (newVersionVal.versionLogs.size > 1) {
@ -156,7 +161,7 @@ fun UpgradeDialog(status: UpdateStatus) {
when (downloadStatusVal) {
is LoadStatus.Loading -> {
AlertDialog(
title = { Text(text = "下载新版本") },
title = { Text(text = "下载") },
text = {
LinearProgressIndicator(
progress = { downloadStatusVal.progress },
@ -177,7 +182,7 @@ fun UpgradeDialog(status: UpdateStatus) {
is LoadStatus.Failure -> {
AlertDialog(
title = { Text(text = "新版本下载失败") },
title = { Text(text = "下载失败") },
text = {
Text(text = downloadStatusVal.exception.let {
it.message ?: it.toString()
@ -196,7 +201,10 @@ fun UpgradeDialog(status: UpdateStatus) {
is LoadStatus.Success -> {
AlertDialog(
title = { Text(text = "新版本下载完毕") },
title = { Text(text = "下载完毕") },
text = {
Text(text = "可继续选择安装新版本")
},
onDismissRequest = {},
dismissButton = {
TextButton(onClick = {
@ -206,9 +214,9 @@ fun UpgradeDialog(status: UpdateStatus) {
}
},
confirmButton = {
TextButton(onClick = throttle(fn = mainVm.viewModelScope.launchAsFn {
AppUtils.installApp(downloadStatusVal.result)
})) {
TextButton(onClick = throttle {
installApk(downloadStatusVal.result)
}) {
Text(text = "安装")
}
})
@ -217,14 +225,13 @@ fun UpgradeDialog(status: UpdateStatus) {
}
}
private fun installApk(file: File) {
val uri = FileProvider.getUriForFile(
app, "${app.packageName}.provider", file
)
val intent = Intent(Intent.ACTION_VIEW)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
intent.setDataAndType(uri, "application/vnd.android.package-archive")
app.tryStartActivity(intent)
}

View File

@ -1,4 +1,30 @@
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<root-path name="root" path="." />
<paths>
<files-path
name="files_path"
path="." />
<cache-path
name="cache_path"
path="." />
<external-path
name="external_path"
path="." />
<external-files-path
name="external_files_path"
path="." />
<external-cache-path
name="external_cache_path"
path="." />
<external-media-path
name="external_media_path"
path="." />
<root-path
name="root"
path="." />
</paths>