Skip to content

Commit

Permalink
feat: app config page
Browse files Browse the repository at this point in the history
  • Loading branch information
lisonge committed Feb 29, 2024
1 parent 99f6684 commit 6d502dd
Show file tree
Hide file tree
Showing 14 changed files with 324 additions and 215 deletions.
15 changes: 6 additions & 9 deletions app/src/main/kotlin/li/songe/gkd/data/AppRule.kt
Original file line number Diff line number Diff line change
@@ -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
Expand Down
12 changes: 4 additions & 8 deletions app/src/main/kotlin/li/songe/gkd/data/GlobalRule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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
Expand Down
19 changes: 12 additions & 7 deletions app/src/main/kotlin/li/songe/gkd/data/RawSubscription.kt
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ data class RawSubscription(
map
}

val appGroups by lazy {
private val appGroups by lazy {
apps.flatMap { a -> a.groups }
}

Expand Down Expand Up @@ -184,13 +184,18 @@ data class RawSubscription(
val excludeMatches: List<String>?
}

@Immutable
interface RawGroupProps : RawCommonProps {
val name: String
val key: Int
val desc: String?
val enable: Boolean?
val scopeKeys: List<Int>?
val rules: List<RawRuleProps>

val valid: Boolean
val errorDesc: String?
val allExampleUrls: List<String>
}

interface RawAppRuleProps {
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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()
Expand Down
12 changes: 7 additions & 5 deletions app/src/main/kotlin/li/songe/gkd/data/ResolvedRule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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

Expand Down
4 changes: 2 additions & 2 deletions app/src/main/kotlin/li/songe/gkd/data/Tuple.kt
Original file line number Diff line number Diff line change
@@ -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,
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/kotlin/li/songe/gkd/service/AbState.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
166 changes: 113 additions & 53 deletions app/src/main/kotlin/li/songe/gkd/ui/AppConfigPage.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,29 +13,40 @@ 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
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)
Expand All @@ -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()
}) {
Expand All @@ -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
}
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))
IconButton(onClick = {}) {
Icon(
imageVector = Icons.Default.MoreVert,
contentDescription = "more",
}
}
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
)
)
}
Spacer(modifier = Modifier.width(10.dp))
Switch(checked = enable, modifier = Modifier, onCheckedChange = {})
}
}
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)
}
}
Loading

0 comments on commit 6d502dd

Please sign in to comment.