feat: app config page

This commit is contained in:
lisonge 2024-02-29 21:10:11 +08:00
parent 99f6684ce4
commit 6d502dd8fb
14 changed files with 327 additions and 218 deletions

View File

@ -1,20 +1,17 @@
package li.songe.gkd.data
import li.songe.gkd.util.ResolvedAppGroup
class AppRule(
rule: RawSubscription.RawAppRule,
subsItem: SubsItem,
group: RawSubscription.RawAppGroup,
rawSubs: RawSubscription,
exclude: String?,
val app: RawSubscription.RawApp,
g: ResolvedAppGroup,
val appInfo: AppInfo?,
) : ResolvedRule(
rule = rule,
group = group,
subsItem = subsItem,
rawSubs = rawSubs,
exclude = exclude,
g = g,
) {
val group = g.group
val app = g.app
val enable = appInfo?.let {
if ((rule.excludeVersionCodes
?: group.excludeVersionCodes)?.contains(appInfo.versionCode) == true

View File

@ -2,6 +2,7 @@ package li.songe.gkd.data
import kotlinx.collections.immutable.ImmutableMap
import li.songe.gkd.service.launcherAppId
import li.songe.gkd.util.ResolvedGlobalGroup
import li.songe.gkd.util.systemAppsFlow
data class GlobalApp(
@ -12,19 +13,14 @@ data class GlobalApp(
)
class GlobalRule(
subsItem: SubsItem,
rule: RawSubscription.RawGlobalRule,
group: RawSubscription.RawGlobalGroup,
rawSubs: RawSubscription,
exclude: String?,
g: ResolvedGlobalGroup,
appInfoCache: ImmutableMap<String, AppInfo>,
) : ResolvedRule(
rule = rule,
group = group,
subsItem = subsItem,
rawSubs = rawSubs,
exclude = exclude,
g = g,
) {
val group = g.group
private val matchAnyApp = rule.matchAnyApp ?: group.matchAnyApp ?: true
private val matchLauncher = rule.matchLauncher ?: group.matchLauncher ?: false
private val matchSystemApp = rule.matchSystemApp ?: group.matchSystemApp ?: false

View File

@ -57,7 +57,7 @@ data class RawSubscription(
map
}
val appGroups by lazy {
private val appGroups by lazy {
apps.flatMap { a -> a.groups }
}
@ -184,6 +184,7 @@ data class RawSubscription(
val excludeMatches: List<String>?
}
@Immutable
interface RawGroupProps : RawCommonProps {
val name: String
val key: Int
@ -191,6 +192,10 @@ data class RawSubscription(
val enable: Boolean?
val scopeKeys: List<Int>?
val rules: List<RawRuleProps>
val valid: Boolean
val errorDesc: String?
val allExampleUrls: List<String>
}
interface RawAppRuleProps {
@ -254,11 +259,11 @@ data class RawSubscription(
(apps ?: emptyList()).associate { a -> a.id to (a.enable ?: true) }
}
val errorDesc by lazy { getErrorDesc() }
override val errorDesc by lazy { getErrorDesc() }
val valid by lazy { errorDesc == null }
override val valid by lazy { errorDesc == null }
val allExampleUrls by lazy {
override val allExampleUrls by lazy {
((exampleUrls ?: emptyList()) + rules.flatMap { r ->
r.exampleUrls ?: emptyList()
}).distinct()
@ -322,11 +327,11 @@ data class RawSubscription(
override val excludeVersionCodes: List<Long>?,
) : RawGroupProps, RawAppRuleProps {
val errorDesc by lazy { getErrorDesc() }
override val errorDesc by lazy { getErrorDesc() }
val valid by lazy { errorDesc == null }
override val valid by lazy { errorDesc == null }
val allExampleUrls by lazy {
override val allExampleUrls by lazy {
((exampleUrls ?: emptyList()) + rules.flatMap { r ->
r.exampleUrls ?: emptyList()
}).distinct()

View File

@ -8,15 +8,17 @@ import li.songe.gkd.service.createCacheTransform
import li.songe.gkd.service.lastTriggerRule
import li.songe.gkd.service.lastTriggerTime
import li.songe.gkd.service.querySelector
import li.songe.gkd.util.ResolvedGroup
import li.songe.selector.Selector
sealed class ResolvedRule(
val rule: RawSubscription.RawRuleProps,
val group: RawSubscription.RawGroupProps,
val rawSubs: RawSubscription,
val subsItem: SubsItem,
val exclude: String?,
val g: ResolvedGroup,
) {
private val group = g.group
val subsItem = g.subsItem
val rawSubs = g.subscription
val config = g.config
val key = rule.key
val index = group.rules.indexOf(rule)
private val preKeys = (rule.preKeys ?: emptyList()).toSet()
@ -198,7 +200,7 @@ sealed class ResolvedRule(
return "id:${subsItem.id}, v:${rawSubs.version}, type:${type}, gKey=${group.key}, gName:${group.name}, index:${index}, key:${key}, status:${status.name}"
}
val excludeData = ExcludeData.parse(exclude)
val excludeData = ExcludeData.parse(config?.exclude)
abstract val type: String

View File

@ -1,8 +1,8 @@
package li.songe.gkd.data
import androidx.compose.runtime.Stable
import androidx.compose.runtime.Immutable
@Stable
@Immutable
data class Tuple3<T0, T1, T2>(
val t0: T0,
val t1: T1,

View File

@ -122,7 +122,7 @@ fun insertClickLog(rule: ResolvedRule) {
activityId = topActivityFlow.value.activityId,
subsId = rule.subsItem.id,
subsVersion = rule.rawSubs.version,
groupKey = rule.group.key,
groupKey = rule.g.group.key,
groupType = when (rule) {
is AppRule -> SubsConfig.AppGroupType
is GlobalRule -> SubsConfig.GlobalGroupType

View File

@ -13,7 +13,6 @@ import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.filled.MoreVert
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
@ -21,21 +20,33 @@ import androidx.compose.material3.Scaffold
import androidx.compose.material3.Switch
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.viewModelScope
import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.annotation.RootNavGraph
import li.songe.gkd.data.ExcludeData
import li.songe.gkd.data.RawSubscription
import li.songe.gkd.data.SubsConfig
import li.songe.gkd.data.stringify
import li.songe.gkd.db.DbSet
import li.songe.gkd.util.LocalNavController
import li.songe.gkd.util.ProfileTransitions
import li.songe.gkd.util.appInfoCacheFlow
import li.songe.gkd.util.launchTry
import li.songe.gkd.util.ruleSummaryFlow
import li.songe.gkd.util.toast
@RootNavGraph
@Destination(style = ProfileTransitions::class)
@ -46,13 +57,11 @@ fun AppConfigPage(appId: String) {
val appInfoCache by appInfoCacheFlow.collectAsState()
val appInfo = appInfoCache[appId]
val ruleSummary by ruleSummaryFlow.collectAsState()
val globalGroups = ruleSummary.globalGroups
val appGroups = ruleSummary.appIdToAllGroups[appId] ?: emptyList()
Scaffold(topBar = {
TopAppBar(navigationIcon = {
val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior()
Scaffold(modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection), topBar = {
TopAppBar(scrollBehavior = scrollBehavior, navigationIcon = {
IconButton(onClick = {
navController.popBackStack()
}) {
@ -69,61 +78,112 @@ fun AppConfigPage(appId: String) {
overflow = TextOverflow.Ellipsis,
)
}, actions = {})
}, content = { contentPadding ->
}) { contentPadding ->
LazyColumn(
modifier = Modifier.padding(contentPadding)
) {
items(appGroups) { (group, enable) ->
Row(
modifier = Modifier
.padding(10.dp, 6.dp)
.fillMaxWidth()
.height(45.dp),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
) {
Column(
modifier = Modifier
.weight(1f)
.fillMaxHeight(),
verticalArrangement = Arrangement.SpaceBetween
) {
Text(
text = group.name,
maxLines = 1,
softWrap = false,
overflow = TextOverflow.Ellipsis,
modifier = Modifier.fillMaxWidth()
)
if (group.valid) {
Text(
text = group.desc ?: "",
maxLines = 1,
softWrap = false,
overflow = TextOverflow.Ellipsis,
modifier = Modifier.fillMaxWidth(),
fontSize = 14.sp
)
} else {
Text(
text = "非法选择器",
modifier = Modifier.fillMaxWidth(),
fontSize = 14.sp,
color = MaterialTheme.colorScheme.error
)
}
items(globalGroups) { g ->
val excludeData = remember(g.config?.exclude) {
ExcludeData.parse(g.config?.exclude)
}
val checked = getChecked(excludeData, g.group, appId, appInfo)
AppGroupCard(g.group, checked ?: false) { newChecked ->
if (checked == null) {
toast("内置禁用,不可修改")
return@AppGroupCard
}
Spacer(modifier = Modifier.width(10.dp))
IconButton(onClick = {}) {
Icon(
imageVector = Icons.Default.MoreVert,
contentDescription = "more",
vm.viewModelScope.launchTry {
DbSet.subsConfigDao.insert(
(g.config ?: SubsConfig(
type = SubsConfig.GlobalGroupType,
subsItemId = g.subsItem.id,
groupKey = g.group.key,
)).copy(
exclude = excludeData.copy(
appIds = excludeData.appIds.toMutableMap().apply {
set(appId, !newChecked)
}
).stringify()
)
)
}
Spacer(modifier = Modifier.width(10.dp))
Switch(checked = enable, modifier = Modifier, onCheckedChange = {})
}
}
items(appGroups) { g ->
AppGroupCard(g.group, g.enable) {
vm.viewModelScope.launchTry {
DbSet.subsConfigDao.insert(
g.config?.copy(enable = it) ?: SubsConfig(
type = SubsConfig.AppGroupType,
subsItemId = g.subsItem.id,
appId = appId,
groupKey = g.group.key,
enable = it
)
)
}
}
}
item {
Spacer(modifier = Modifier.height(40.dp))
if (globalGroups.size + appGroups.size == 0) {
Text(
text = "暂无规则",
modifier = Modifier.fillMaxWidth(),
textAlign = TextAlign.Center
)
}
}
}
})
}
}
@Composable
private fun AppGroupCard(
group: RawSubscription.RawGroupProps,
enable: Boolean,
onCheckedChange: ((Boolean) -> Unit)?,
) {
Row(
modifier = Modifier
.padding(10.dp, 6.dp)
.fillMaxWidth()
.height(45.dp),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
) {
Column(
modifier = Modifier
.weight(1f)
.fillMaxHeight(),
verticalArrangement = Arrangement.SpaceBetween
) {
Text(
text = group.name,
maxLines = 1,
softWrap = false,
overflow = TextOverflow.Ellipsis,
modifier = Modifier.fillMaxWidth()
)
if (group.valid) {
Text(
text = group.desc ?: "",
maxLines = 1,
softWrap = false,
overflow = TextOverflow.Ellipsis,
modifier = Modifier.fillMaxWidth(),
fontSize = 14.sp
)
} else {
Text(
text = "非法选择器",
modifier = Modifier.fillMaxWidth(),
fontSize = 14.sp,
color = MaterialTheme.colorScheme.error
)
}
}
Spacer(modifier = Modifier.width(10.dp))
Switch(checked = enable, modifier = Modifier, onCheckedChange = onCheckedChange)
}
}

View File

@ -32,6 +32,7 @@ import androidx.compose.material3.Switch
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
@ -42,6 +43,7 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
@ -113,8 +115,9 @@ fun AppItemPage(
mutableStateOf<RawSubscription.RawAppGroup?>(null)
}
Scaffold(topBar = {
TopAppBar(navigationIcon = {
val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior()
Scaffold(modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection), topBar = {
TopAppBar(scrollBehavior = scrollBehavior, navigationIcon = {
IconButton(onClick = {
navController.popBackStack()
}) {
@ -145,7 +148,7 @@ fun AppItemPage(
)
}
}
}, content = { contentPadding ->
}) { contentPadding ->
LazyColumn(
modifier = Modifier
.fillMaxSize()
@ -213,9 +216,9 @@ fun AppItemPage(
val groupEnable = getGroupRawEnable(
group,
subsConfigs,
subsConfigs.find { c -> c.groupKey == group.key },
groupToCategoryMap[group],
categoryConfigs
categoryConfigs.find { c -> c.categoryKey == groupToCategoryMap[group]?.key }
)
val subsConfig = subsConfigs.find { it.groupKey == group.key }
Switch(
@ -238,7 +241,7 @@ fun AppItemPage(
Spacer(modifier = Modifier.height(20.dp))
}
}
})
}
showGroupItem?.let { showGroupItemVal ->

View File

@ -29,6 +29,7 @@ import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
@ -37,6 +38,7 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
@ -83,8 +85,9 @@ fun CategoryPage(subsItemId: Long) {
val categories = subsRaw?.categories ?: emptyList()
val categoriesGroups = subsRaw?.categoryToGroupsMap ?: emptyMap()
Scaffold(topBar = {
TopAppBar(navigationIcon = {
val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior()
Scaffold(modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection), topBar = {
TopAppBar(scrollBehavior = scrollBehavior, navigationIcon = {
IconButton(onClick = {
navController.popBackStack()
}) {
@ -103,7 +106,7 @@ fun CategoryPage(subsItemId: Long) {
)
}
}
}, content = { contentPadding ->
}) { contentPadding ->
LazyColumn(
modifier = Modifier.padding(contentPadding)
) {
@ -167,7 +170,7 @@ fun CategoryPage(subsItemId: Long) {
}
}
}
})
}
editEnableCategory?.let { category ->
val categoryConfig =

View File

@ -31,6 +31,7 @@ import androidx.compose.material3.Switch
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
@ -40,6 +41,7 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
@ -93,10 +95,11 @@ fun GlobalRulePage(subsItemId: Long, focusGroupKey: Int? = null) {
null
)
}
val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior()
Scaffold(
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
topBar = {
TopAppBar(navigationIcon = {
TopAppBar(scrollBehavior = scrollBehavior, navigationIcon = {
IconButton(onClick = {
navController.popBackStack()
}) {
@ -108,7 +111,8 @@ fun GlobalRulePage(subsItemId: Long, focusGroupKey: Int? = null) {
}, title = {
Text(text = "${rawSubs?.name ?: subsItemId}/全局规则")
})
}, floatingActionButton = {
},
floatingActionButton = {
if (editable) {
FloatingActionButton(onClick = { showAddDlg = true }) {
Icon(
@ -118,103 +122,102 @@ fun GlobalRulePage(subsItemId: Long, focusGroupKey: Int? = null) {
}
}
},
content = { paddingValues ->
LazyColumn(modifier = Modifier.padding(paddingValues)) {
items(globalGroups, { g -> g.key }) { group ->
Row(
) { paddingValues ->
LazyColumn(modifier = Modifier.padding(paddingValues)) {
items(globalGroups, { g -> g.key }) { group ->
Row(
modifier = Modifier
.background(
if (group.key == focusGroupKey) MaterialTheme.colorScheme.inversePrimary else Color.Transparent
)
.clickable { setShowGroupItem(group) }
.padding(10.dp, 6.dp)
.fillMaxWidth()
.height(45.dp),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
) {
Column(
modifier = Modifier
.background(
if (group.key == focusGroupKey) MaterialTheme.colorScheme.inversePrimary else Color.Transparent
)
.clickable { setShowGroupItem(group) }
.padding(10.dp, 6.dp)
.fillMaxWidth()
.height(45.dp),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
.weight(1f)
.fillMaxHeight(),
verticalArrangement = Arrangement.SpaceBetween
) {
Column(
modifier = Modifier
.weight(1f)
.fillMaxHeight(),
verticalArrangement = Arrangement.SpaceBetween
) {
Text(
text = group.name,
maxLines = 1,
softWrap = false,
overflow = TextOverflow.Ellipsis,
modifier = Modifier.fillMaxWidth()
)
if (group.valid) {
Text(
text = group.name,
text = group.desc ?: "",
maxLines = 1,
softWrap = false,
overflow = TextOverflow.Ellipsis,
modifier = Modifier.fillMaxWidth()
modifier = Modifier.fillMaxWidth(),
fontSize = 14.sp
)
if (group.valid) {
Text(
text = group.desc ?: "",
maxLines = 1,
softWrap = false,
overflow = TextOverflow.Ellipsis,
modifier = Modifier.fillMaxWidth(),
fontSize = 14.sp
)
} else {
Text(
text = "非法选择器",
modifier = Modifier.fillMaxWidth(),
fontSize = 14.sp,
color = MaterialTheme.colorScheme.error
)
}
}
Spacer(modifier = Modifier.width(10.dp))
IconButton(onClick = {
if (editable) {
setMenuGroupRaw(group)
} else {
navController.navigate(
GlobalRuleExcludePageDestination(
subsItemId,
group.key
)
)
}
}) {
Icon(
imageVector = Icons.Default.MoreVert,
contentDescription = null,
} else {
Text(
text = "非法选择器",
modifier = Modifier.fillMaxWidth(),
fontSize = 14.sp,
color = MaterialTheme.colorScheme.error
)
}
Spacer(modifier = Modifier.width(10.dp))
}
Spacer(modifier = Modifier.width(10.dp))
val groupEnable = subsConfigs.find { c -> c.groupKey == group.key }?.enable
?: group.enable ?: true
val subsConfig = subsConfigs.find { it.groupKey == group.key }
Switch(
checked = groupEnable, modifier = Modifier,
onCheckedChange = vm.viewModelScope.launchAsFn { enable ->
val newItem = (subsConfig?.copy(enable = enable) ?: SubsConfig(
type = SubsConfig.GlobalGroupType,
subsItemId = subsItemId,
groupKey = group.key,
enable = enable
))
DbSet.subsConfigDao.insert(newItem)
}
IconButton(onClick = {
if (editable) {
setMenuGroupRaw(group)
} else {
navController.navigate(
GlobalRuleExcludePageDestination(
subsItemId,
group.key
)
)
}
}) {
Icon(
imageVector = Icons.Default.MoreVert,
contentDescription = null,
)
}
Spacer(modifier = Modifier.width(10.dp))
val groupEnable = subsConfigs.find { c -> c.groupKey == group.key }?.enable
?: group.enable ?: true
val subsConfig = subsConfigs.find { it.groupKey == group.key }
Switch(
checked = groupEnable, modifier = Modifier,
onCheckedChange = vm.viewModelScope.launchAsFn { enable ->
val newItem = (subsConfig?.copy(enable = enable) ?: SubsConfig(
type = SubsConfig.GlobalGroupType,
subsItemId = subsItemId,
groupKey = group.key,
enable = enable
))
DbSet.subsConfigDao.insert(newItem)
}
)
}
item {
Spacer(modifier = Modifier.height(40.dp))
if (globalGroups.isEmpty()) {
Text(
text = "暂无规则",
modifier = Modifier.fillMaxWidth(),
textAlign = TextAlign.Center
)
}
}
item {
Spacer(modifier = Modifier.height(40.dp))
if (globalGroups.isEmpty()) {
Text(
text = "暂无规则",
modifier = Modifier.fillMaxWidth(),
textAlign = TextAlign.Center
)
}
}
}
)
}
if (showAddDlg && rawSubs != null) {
var source by remember {

View File

@ -101,7 +101,12 @@ class SubsVm @Inject constructor(stateHandle: SavedStateHandle) : ViewModel() {
apps.map { app ->
val appGroupSubsConfigs = groupSubsConfigs.filter { s -> s.appId == app.id }
val enableSize = app.groups.count { g ->
getGroupRawEnable(g, appGroupSubsConfigs, groupToCategoryMap[g], categoryConfigs)
getGroupRawEnable(
g,
appGroupSubsConfigs.find { c -> c.groupKey == g.key },
groupToCategoryMap[g],
categoryConfigs.find { c -> c.categoryKey == groupToCategoryMap[g]?.key }
)
}
Tuple3(app, appSubsConfigs.find { s -> s.appId == app.id }, enableSize)
}

View File

@ -244,7 +244,7 @@ fun useAppListPage(): ScaffoldExt {
val appGroups = ruleSummary.appIdToAllGroups[appInfo.id] ?: emptyList()
val appDesc = if (appGroups.isNotEmpty()) {
when (val disabledCount = appGroups.count { g -> !g.second }) {
when (val disabledCount = appGroups.count { g -> !g.enable }) {
0 -> {
"${appGroups.size}组规则"
}

View File

@ -0,0 +1,28 @@
package li.songe.gkd.util
import li.songe.gkd.data.RawSubscription
import li.songe.gkd.data.SubsConfig
import li.songe.gkd.data.SubsItem
sealed class ResolvedGroup(
open val group: RawSubscription.RawGroupProps,
val subscription: RawSubscription,
val subsItem: SubsItem,
val config: SubsConfig?,
)
class ResolvedAppGroup(
override val group: RawSubscription.RawAppGroup,
subscription: RawSubscription,
subsItem: SubsItem,
config: SubsConfig?,
val app: RawSubscription.RawApp,
val enable: Boolean,
) : ResolvedGroup(group, subscription, subsItem, config)
class ResolvedGlobalGroup(
override val group: RawSubscription.RawGlobalGroup,
subscription: RawSubscription,
subsItem: SubsItem,
config: SubsConfig?,
) : ResolvedGroup(group, subscription, subsItem, config)

View File

@ -64,16 +64,14 @@ fun deleteSubscription(subsId: Long) {
}
fun getGroupRawEnable(
rawGroup: RawSubscription.RawGroupProps,
subsConfigs: List<SubsConfig>,
group: RawSubscription.RawGroupProps,
subsConfig: SubsConfig?,
category: RawSubscription.RawCategory?,
categoryConfigs: List<CategoryConfig>
categoryConfig: CategoryConfig?,
): Boolean {
// 优先级: 规则用户配置 > 批量配置 > 批量默认 > 规则默认
val groupConfig = subsConfigs.find { c -> c.groupKey == rawGroup.key }
// 1.规则用户配置
return groupConfig?.enable ?: if (category != null) {// 这个规则被批量配置捕获
val categoryConfig = categoryConfigs.find { c -> c.categoryKey == category.key }
return subsConfig?.enable ?: if (category != null) {// 这个规则被批量配置捕获
val enable = if (categoryConfig != null) {
// 2.批量配置
categoryConfig.enable
@ -84,15 +82,15 @@ fun getGroupRawEnable(
enable
} else {
null
} ?: rawGroup.enable ?: true
} ?: group.enable ?: true
}
data class RuleSummary(
val globalRules: ImmutableList<GlobalRule> = persistentListOf(),
val globalGroups: ImmutableList<RawSubscription.RawGlobalGroup> = persistentListOf(),
val globalGroups: ImmutableList<ResolvedGlobalGroup> = persistentListOf(),
val appIdToRules: ImmutableMap<String, ImmutableList<AppRule>> = persistentMapOf(),
val appIdToGroups: ImmutableMap<String, ImmutableList<RawSubscription.RawAppGroup>> = persistentMapOf(),
val appIdToAllGroups: ImmutableMap<String, ImmutableList<Pair<RawSubscription.RawAppGroup, Boolean>>> = persistentMapOf(),
val appIdToAllGroups: ImmutableMap<String, ImmutableList<ResolvedAppGroup>> = persistentMapOf(),
) {
private val appSize = appIdToRules.keys.size
private val appGroupSize = appIdToGroups.values.sumOf { s -> s.size }
@ -116,7 +114,8 @@ data class RuleSummary(
}
val slowGlobalGroups =
globalRules.filter { r -> r.isSlow }.distinctBy { r -> r.group }.map { r -> r.group to r }
globalRules.filter { r -> r.isSlow }.distinctBy { r -> r.group }
.map { r -> r.group to r }
val slowAppGroups =
appIdToRules.values.flatten().filter { r -> r.isSlow }.distinctBy { r -> r.group }
.map { r -> r.group to r }
@ -134,11 +133,12 @@ val ruleSummaryFlow by lazy {
val globalSubsConfigs = subsConfigs.filter { c -> c.type == SubsConfig.GlobalGroupType }
val appSubsConfigs = subsConfigs.filter { c -> c.type == SubsConfig.AppType }
val groupSubsConfigs = subsConfigs.filter { c -> c.type == SubsConfig.AppGroupType }
val appRules = mutableMapOf<String, MutableList<AppRule>>()
val appGroups = mutableMapOf<String, List<RawSubscription.RawAppGroup>>()
val appAllGroups = mutableMapOf<String, List<Pair<RawSubscription.RawAppGroup, Boolean>>>()
val appRules = HashMap<String, MutableList<AppRule>>()
val appGroups = HashMap<String, List<RawSubscription.RawAppGroup>>()
val appAllGroups =
HashMap<String, List<ResolvedAppGroup>>()
val globalRules = mutableListOf<GlobalRule>()
val globalGroups = mutableListOf<RawSubscription.RawGlobalGroup>()
val globalGroups = mutableListOf<ResolvedGlobalGroup>()
subsItems.filter { it.enable }.forEach { subsItem ->
val rawSubs = subsIdToRaw[subsItem.id] ?: return@forEach
@ -150,14 +150,18 @@ val ruleSummaryFlow by lazy {
g.valid && (subGlobalSubsConfigs.find { c -> c.groupKey == g.key }?.enable
?: g.enable ?: true)
}.forEach { groupRaw ->
globalGroups.add(groupRaw)
val config = subGlobalSubsConfigs.find { c -> c.groupKey == groupRaw.key }
val g = ResolvedGlobalGroup(
group = groupRaw,
subscription = rawSubs,
subsItem = subsItem,
config = config
)
globalGroups.add(g)
val subRules = groupRaw.rules.map { ruleRaw ->
GlobalRule(
rule = ruleRaw,
group = groupRaw,
rawSubs = rawSubs,
subsItem = subsItem,
exclude = subGlobalSubsConfigs.find { c -> c.groupKey == groupRaw.key }?.exclude,
g = g,
appInfoCache = appInfoCache,
)
}
@ -183,31 +187,34 @@ val ruleSummaryFlow by lazy {
val subAppGroups = mutableListOf<RawSubscription.RawAppGroup>()
val appGroupConfigs = subGroupSubsConfigs.filter { c -> c.appId == appRaw.id }
val subAppGroupToRules = mutableMapOf<RawSubscription.RawAppGroup, List<AppRule>>()
val groupAndEnables = appRaw.groups.map { groupRaw ->
val enable = groupRaw.valid && getGroupRawEnable(
groupRaw,
appGroupConfigs,
rawSubs.groupToCategoryMap[groupRaw],
subCategoryConfigs
val groupAndEnables = appRaw.groups.map { group ->
val enable = group.valid && getGroupRawEnable(
group,
appGroupConfigs.find { c -> c.groupKey == group.key },
rawSubs.groupToCategoryMap[group],
subCategoryConfigs.find { c -> c.categoryKey == rawSubs.groupToCategoryMap[group]?.key }
)
ResolvedAppGroup(
group = group,
subscription = rawSubs,
subsItem = subsItem,
config = appGroupConfigs.find { c -> c.groupKey == group.key },
app = appRaw,
enable = enable
)
groupRaw to enable
}
appAllGroups[appRaw.id] = (appAllGroups[appRaw.id] ?: emptyList()) + groupAndEnables
groupAndEnables.forEach { (groupRaw, enable) ->
if (enable) {
subAppGroups.add(groupRaw)
val subRules = groupRaw.rules.map { ruleRaw ->
groupAndEnables.forEach { g ->
if (g.enable) {
subAppGroups.add(g.group)
val subRules = g.group.rules.map { ruleRaw ->
AppRule(
rule = ruleRaw,
group = groupRaw,
app = appRaw,
rawSubs = rawSubs,
subsItem = subsItem,
exclude = appGroupConfigs.find { c -> c.groupKey == groupRaw.key }?.exclude,
g = g,
appInfo = appInfoCache[appRaw.id]
)
}.filter { r -> r.enable }
subAppGroupToRules[groupRaw] = subRules
subAppGroupToRules[g.group] = subRules
if (subRules.isNotEmpty()) {
val rules = appRules[appRaw.id] ?: mutableListOf()
appRules[appRaw.id] = rules