diff --git a/app/src/main/kotlin/li/songe/gkd/service/ManageService.kt b/app/src/main/kotlin/li/songe/gkd/service/ManageService.kt index 261c3e6..8d1127c 100644 --- a/app/src/main/kotlin/li/songe/gkd/service/ManageService.kt +++ b/app/src/main/kotlin/li/songe/gkd/service/ManageService.kt @@ -6,6 +6,7 @@ import androidx.core.app.NotificationManagerCompat import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.debounce import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch import li.songe.gkd.app @@ -16,6 +17,7 @@ import li.songe.gkd.notif.abNotif import li.songe.gkd.notif.createNotif import li.songe.gkd.notif.defaultChannel import li.songe.gkd.util.clickCountFlow +import li.songe.gkd.util.getSubsStatus import li.songe.gkd.util.ruleSummaryFlow import li.songe.gkd.util.storeFlow @@ -26,22 +28,22 @@ class ManageService : CompositionService({ val scope = useScope() scope.launch { combine( + GkdAbService.isRunning, + storeFlow, ruleSummaryFlow, clickCountFlow, - storeFlow, - GkdAbService.isRunning - ) { allRules, clickCount, store, abRunning -> + ) { abRunning, store, ruleSummary, count -> if (!abRunning) return@combine "无障碍未授权" if (!store.enableService) return@combine "服务已暂停" if (store.useCustomNotifText) { - return@combine store.customNotifText.replace("$" + "{count}", clickCount.toString()) + return@combine store.customNotifText + .replace("\${a}", ruleSummary.globalGroups.size.toString()) + .replace("\${b}", ruleSummary.appSize.toString()) + .replace("\${c}", ruleSummary.appGroupSize.toString()) + .replace("\${d}", count.toString()) } - allRules.numText + if (clickCount > 0) { - "/${clickCount}点击" - } else { - "" - } - }.stateIn(scope, SharingStarted.Eagerly, "").collect { text -> + return@combine getSubsStatus(ruleSummary, count) + }.debounce(500L).stateIn(scope, SharingStarted.Eagerly, "").collect { text -> createNotif( context, defaultChannel.id, abNotif.copy( text = text diff --git a/app/src/main/kotlin/li/songe/gkd/ui/home/HomeVm.kt b/app/src/main/kotlin/li/songe/gkd/ui/home/HomeVm.kt index 18bb555..bd71abc 100644 --- a/app/src/main/kotlin/li/songe/gkd/ui/home/HomeVm.kt +++ b/app/src/main/kotlin/li/songe/gkd/ui/home/HomeVm.kt @@ -21,6 +21,7 @@ import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.debounce import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn +import li.songe.gkd.appScope import li.songe.gkd.data.GithubPoliciesAsset import li.songe.gkd.data.RawSubscription import li.songe.gkd.data.RpcError @@ -32,6 +33,7 @@ import li.songe.gkd.util.SortTypeOption import li.songe.gkd.util.appInfoCacheFlow import li.songe.gkd.util.clickCountFlow import li.songe.gkd.util.client +import li.songe.gkd.util.getSubsStatus import li.songe.gkd.util.launchTry import li.songe.gkd.util.map import li.songe.gkd.util.orderedAppInfosFlow @@ -105,13 +107,11 @@ class HomeVm @Inject constructor() : ViewModel() { } }.stateIn(viewModelScope, SharingStarted.Eagerly, null) - val subsStatusFlow = combine(ruleSummaryFlow, clickCountFlow) { allRules, clickCount -> - allRules.numText + if (clickCount > 0) { - "/${clickCount}点击" - } else { - "" - } - }.stateIn(viewModelScope, SharingStarted.Eagerly, "") + val subsStatusFlow by lazy { + combine(ruleSummaryFlow, clickCountFlow) { ruleSummary, count -> + getSubsStatus(ruleSummary, count) + }.stateIn(appScope, SharingStarted.Eagerly, "") + } fun addSubsFromUrl(url: String) = viewModelScope.launchTry(Dispatchers.IO) { if (subsRefreshingFlow.value) return@launchTry diff --git a/app/src/main/kotlin/li/songe/gkd/ui/home/SettingsPage.kt b/app/src/main/kotlin/li/songe/gkd/ui/home/SettingsPage.kt index d983719..6052151 100644 --- a/app/src/main/kotlin/li/songe/gkd/ui/home/SettingsPage.kt +++ b/app/src/main/kotlin/li/songe/gkd/ui/home/SettingsPage.kt @@ -12,10 +12,13 @@ import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.outlined.HelpOutline import androidx.compose.material.icons.filled.Upload import androidx.compose.material.icons.outlined.Settings import androidx.compose.material3.AlertDialog import androidx.compose.material3.Card +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton import androidx.compose.material3.LinearProgressIndicator import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedTextField @@ -50,6 +53,7 @@ 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.buildDialogOptions import li.songe.gkd.ui.destinations.AboutPageDestination import li.songe.gkd.ui.destinations.AdvancedPageDestination import li.songe.gkd.ui.style.EmptyHeight @@ -107,9 +111,7 @@ fun useSettingsPage(): ScaffoldExt { OutlinedTextField( value = value, placeholder = { - Text( - text = "请输入提示内容", - ) + Text(text = "请输入提示内容") }, onValueChange = { value = it.take(maxCharLen) @@ -145,7 +147,28 @@ fun useSettingsPage(): ScaffoldExt { mutableStateOf(store.customNotifText) } val maxCharLen = 64 - AlertDialog(title = { Text(text = "通知文案") }, text = { + AlertDialog(title = { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween, + modifier = Modifier.fillMaxWidth(), + ) { + Text(text = "通知文案") + IconButton(onClick = throttle { + mainVm.dialogFlow.value = buildDialogOptions( + title = "文案规则", + text = "通知文案支持变量替换,规则如下\n\${a} 全局规则数\n\${b} 应用数\n\${c} 应用规则组数\n\${d} 触发次数\n\n示例模板\n\${a}全局/\${b}应用/\${c}规则组/\${d}触发\n\n替换结果\n0全局/1应用/2规则组/3触发", + confirmText = "知道了", + confirmAction = { mainVm.dialogFlow.value = null }, + ) + }) { + Icon( + imageVector = Icons.AutoMirrored.Outlined.HelpOutline, + contentDescription = null, + ) + } + } + }, text = { OutlinedTextField( value = value, placeholder = { diff --git a/app/src/main/kotlin/li/songe/gkd/util/Store.kt b/app/src/main/kotlin/li/songe/gkd/util/Store.kt index 0a8fbfe..10166e4 100644 --- a/app/src/main/kotlin/li/songe/gkd/util/Store.kt +++ b/app/src/main/kotlin/li/songe/gkd/util/Store.kt @@ -65,7 +65,7 @@ data class Store( val showSaveSnapshotToast: Boolean = true, val useSystemToast: Boolean = false, val useCustomNotifText: Boolean = false, - val customNotifText: String = "GKD", + val customNotifText: String = "\${a}全局/\${b}应用/\${c}规则组/\${d}触发", ) val storeFlow by lazy { diff --git a/app/src/main/kotlin/li/songe/gkd/util/SubsState.kt b/app/src/main/kotlin/li/songe/gkd/util/SubsState.kt index af2d612..6df06cc 100644 --- a/app/src/main/kotlin/li/songe/gkd/util/SubsState.kt +++ b/app/src/main/kotlin/li/songe/gkd/util/SubsState.kt @@ -131,8 +131,8 @@ data class RuleSummary( val appIdToGroups: ImmutableMap> = persistentMapOf(), val appIdToAllGroups: ImmutableMap> = persistentMapOf(), ) { - private val appSize = appIdToRules.keys.size - private val appGroupSize = appIdToGroups.values.sumOf { s -> s.size } + val appSize = appIdToRules.keys.size + val appGroupSize = appIdToGroups.values.sumOf { s -> s.size } val numText = if (globalGroups.size + appGroupSize > 0) { if (globalGroups.isNotEmpty()) { @@ -285,6 +285,14 @@ val ruleSummaryFlow by lazy { }.flowOn(Dispatchers.Default).stateIn(appScope, SharingStarted.Eagerly, RuleSummary()) } +fun getSubsStatus(ruleSummary: RuleSummary, count: Int): String { + return if (count > 0) { + "${ruleSummary.numText}/${count}触发" + } else { + ruleSummary.numText + } +} + private fun loadSubs(id: Long): RawSubscription { val file = subsFolder.resolve("${id}.json") if (!file.exists()) {