perf: save filter settings
Some checks are pending
Build-Apk / build (push) Waiting to run

This commit is contained in:
lisonge 2024-10-03 01:24:02 +08:00
parent 76b3049b36
commit 8892dd3da8
10 changed files with 110 additions and 69 deletions

View File

@ -74,6 +74,7 @@ import li.songe.gkd.util.ResolvedGroup
import li.songe.gkd.util.RuleSortOption import li.songe.gkd.util.RuleSortOption
import li.songe.gkd.util.appInfoCacheFlow import li.songe.gkd.util.appInfoCacheFlow
import li.songe.gkd.util.launchTry import li.songe.gkd.util.launchTry
import li.songe.gkd.util.storeFlow
import li.songe.gkd.util.throttle import li.songe.gkd.util.throttle
@Destination<RootGraph>(style = ProfileTransitions::class) @Destination<RootGraph>(style = ProfileTransitions::class)
@ -148,12 +149,12 @@ fun AppConfigPage(appId: String) {
RadioButton( RadioButton(
selected = ruleSortType == s, selected = ruleSortType == s,
onClick = { onClick = {
vm.ruleSortTypeFlow.update { s } storeFlow.update { it.copy(appRuleSortType = s.value) }
} }
) )
}, },
onClick = { onClick = {
vm.ruleSortTypeFlow.update { s } storeFlow.update { it.copy(appRuleSortType = s.value) }
}, },
) )
} }
@ -164,7 +165,8 @@ fun AppConfigPage(appId: String) {
floatingActionButton = { floatingActionButton = {
FloatingActionButton( FloatingActionButton(
onClick = throttle { onClick = throttle {
navController.toDestinationsNavigator().navigate(AppItemPageDestination(LOCAL_SUBS_ID, appId)) navController.toDestinationsNavigator()
.navigate(AppItemPageDestination(LOCAL_SUBS_ID, appId))
}, },
content = { content = {
Icon( Icon(
@ -295,7 +297,6 @@ private fun AppGroupCard(
onClick: () -> Unit, onClick: () -> Unit,
onCheckedChange: ((Boolean) -> Unit)?, onCheckedChange: ((Boolean) -> Unit)?,
) { ) {
val context = LocalContext.current as MainActivity
Row( Row(
modifier = Modifier modifier = Modifier
.clickable(onClick = onClick) .clickable(onClick = onClick)
@ -349,20 +350,26 @@ private fun AppGroupCard(
if (checked != null) { if (checked != null) {
Switch(checked = checked, onCheckedChange = onCheckedChange) Switch(checked = checked, onCheckedChange = onCheckedChange)
} else { } else {
Switch( InnerDisableSwitch()
checked = false,
enabled = false,
onCheckedChange = null,
modifier = Modifier.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = null,
) {
context.mainVm.dialogFlow.updateDialogOptions(
title = "内置禁用",
text = "此规则组已经在其 apps 字段中配置对当前应用的禁用, 因此无法手动开启规则组\n\n提示: 这种情况一般在此全局规则无法适配/跳过适配/单独适配当前应用时出现",
)
}
)
} }
} }
}
@Composable
fun InnerDisableSwitch() {
val context = LocalContext.current as MainActivity
Switch(
checked = false,
enabled = false,
onCheckedChange = null,
modifier = Modifier.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = null,
) {
context.mainVm.dialogFlow.updateDialogOptions(
title = "内置禁用",
text = "此规则组已经在其 apps 字段中配置对当前应用的禁用, 因此无法手动开启规则组\n\n提示: 这种情况一般在此全局规则无法适配/跳过适配/单独适配当前应用时出现",
)
}
)
} }

View File

@ -4,7 +4,6 @@ import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.ramcosta.composedestinations.generated.destinations.AppConfigPageDestination import com.ramcosta.composedestinations.generated.destinations.AppConfigPageDestination
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flatMapLatest
@ -16,11 +15,14 @@ import li.songe.gkd.util.ResolvedAppGroup
import li.songe.gkd.util.ResolvedGlobalGroup import li.songe.gkd.util.ResolvedGlobalGroup
import li.songe.gkd.util.RuleSortOption import li.songe.gkd.util.RuleSortOption
import li.songe.gkd.util.collator import li.songe.gkd.util.collator
import li.songe.gkd.util.findOption
import li.songe.gkd.util.getGroupRawEnable import li.songe.gkd.util.getGroupRawEnable
import li.songe.gkd.util.map
import li.songe.gkd.util.storeFlow
import li.songe.gkd.util.subsIdToRawFlow import li.songe.gkd.util.subsIdToRawFlow
import li.songe.gkd.util.subsItemsFlow import li.songe.gkd.util.subsItemsFlow
class AppConfigVm (stateHandle: SavedStateHandle) : ViewModel() { class AppConfigVm(stateHandle: SavedStateHandle) : ViewModel() {
private val args = AppConfigPageDestination.argsFrom(stateHandle) private val args = AppConfigPageDestination.argsFrom(stateHandle)
private val latestGlobalLogsFlow = DbSet.clickLogDao.queryAppLatest( private val latestGlobalLogsFlow = DbSet.clickLogDao.queryAppLatest(
@ -33,7 +35,8 @@ class AppConfigVm (stateHandle: SavedStateHandle) : ViewModel() {
SubsConfig.AppGroupType SubsConfig.AppGroupType
) )
val ruleSortTypeFlow = MutableStateFlow<RuleSortOption>(RuleSortOption.Default) val ruleSortTypeFlow =
storeFlow.map(viewModelScope) { RuleSortOption.allSubObject.findOption(it.appRuleSortType) }
private val subsFlow = combine(subsIdToRawFlow, subsItemsFlow) { subsIdToRaw, subsItems -> private val subsFlow = combine(subsIdToRawFlow, subsItemsFlow) { subsIdToRaw, subsItems ->
subsItems.mapNotNull { if (it.enable && subsIdToRaw[it.id] != null) it to subsIdToRaw[it.id]!! else null } subsItems.mapNotNull { if (it.enable && subsIdToRaw[it.id] != null) it to subsIdToRaw[it.id]!! else null }

View File

@ -259,7 +259,7 @@ fun AuthA11yPage() {
} }
} }
private val commandText by lazy { "adb pm grant ${META.appId} android.permission.WRITE_SECURE_SETTINGS" } private val commandText by lazy { "adb shell pm grant ${META.appId} android.permission.WRITE_SECURE_SETTINGS" }
private suspend fun MainActivity.grantPermissionByShizuku() { private suspend fun MainActivity.grantPermissionByShizuku() {
if (shizukuOkState.stateFlow.value) { if (shizukuOkState.stateFlow.value) {

View File

@ -61,6 +61,7 @@ import androidx.lifecycle.viewmodel.compose.viewModel
import com.google.accompanist.drawablepainter.rememberDrawablePainter import com.google.accompanist.drawablepainter.rememberDrawablePainter
import com.ramcosta.composedestinations.annotation.Destination import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.annotation.RootGraph import com.ramcosta.composedestinations.annotation.RootGraph
import kotlinx.coroutines.flow.update
import li.songe.gkd.data.AppInfo import li.songe.gkd.data.AppInfo
import li.songe.gkd.data.ExcludeData import li.songe.gkd.data.ExcludeData
import li.songe.gkd.data.RawSubscription import li.songe.gkd.data.RawSubscription
@ -79,6 +80,8 @@ import li.songe.gkd.util.LocalNavController
import li.songe.gkd.util.ProfileTransitions import li.songe.gkd.util.ProfileTransitions
import li.songe.gkd.util.SortTypeOption import li.songe.gkd.util.SortTypeOption
import li.songe.gkd.util.launchTry import li.songe.gkd.util.launchTry
import li.songe.gkd.util.mapHashCode
import li.songe.gkd.util.storeFlow
import li.songe.gkd.util.toast import li.songe.gkd.util.toast
@Destination<RootGraph>(style = ProfileTransitions::class) @Destination<RootGraph>(style = ProfileTransitions::class)
@ -109,9 +112,16 @@ fun GlobalRuleExcludePage(subsItemId: Long, groupKey: Int) {
}) })
val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior() val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior()
val listState = rememberLazyListState() val listState = rememberLazyListState()
LaunchedEffect(key1 = showAppInfos, block = { var isFirstVisit by remember { mutableStateOf(true) }
listState.animateScrollToItem(0) LaunchedEffect(
}) key1 = showAppInfos.mapHashCode { it.id },
) {
if (isFirstVisit) {
isFirstVisit = false
} else {
listState.scrollToItem(0)
}
}
var expanded by remember { mutableStateOf(false) } var expanded by remember { mutableStateOf(false) }
Scaffold(modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection), topBar = { Scaffold(modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection), topBar = {
TopAppBar(scrollBehavior = scrollBehavior, navigationIcon = { TopAppBar(scrollBehavior = scrollBehavior, navigationIcon = {
@ -190,12 +200,12 @@ fun GlobalRuleExcludePage(subsItemId: Long, groupKey: Int) {
trailingIcon = { trailingIcon = {
RadioButton(selected = sortType == sortOption, RadioButton(selected = sortType == sortOption,
onClick = { onClick = {
vm.sortTypeFlow.value = sortOption storeFlow.update { it.copy(subsExcludeSortType = sortOption.value) }
} }
) )
}, },
onClick = { onClick = {
vm.sortTypeFlow.value = sortOption storeFlow.update { it.copy(subsExcludeSortType = sortOption.value) }
}, },
) )
} }
@ -213,11 +223,11 @@ fun GlobalRuleExcludePage(subsItemId: Long, groupKey: Int) {
Checkbox( Checkbox(
checked = showSystemApp, checked = showSystemApp,
onCheckedChange = { onCheckedChange = {
vm.showSystemAppFlow.value = !vm.showSystemAppFlow.value storeFlow.update { it.copy(subsExcludeShowSystemApp = !it.subsExcludeShowSystemApp) }
}) })
}, },
onClick = { onClick = {
vm.showSystemAppFlow.value = !vm.showSystemAppFlow.value storeFlow.update { it.copy(subsExcludeShowSystemApp = !it.subsExcludeShowSystemApp) }
}, },
) )
DropdownMenuItem( DropdownMenuItem(
@ -228,12 +238,11 @@ fun GlobalRuleExcludePage(subsItemId: Long, groupKey: Int) {
Checkbox( Checkbox(
checked = showHiddenApp, checked = showHiddenApp,
onCheckedChange = { onCheckedChange = {
vm.showHiddenAppFlow.value = storeFlow.update { it.copy(subsExcludeShowHiddenApp = !it.subsExcludeShowHiddenApp) }
!vm.showHiddenAppFlow.value
}) })
}, },
onClick = { onClick = {
vm.showHiddenAppFlow.value = !vm.showHiddenAppFlow.value storeFlow.update { it.copy(subsExcludeShowHiddenApp = !it.subsExcludeShowHiddenApp) }
}, },
) )
} }
@ -334,11 +343,7 @@ fun GlobalRuleExcludePage(subsItemId: Long, groupKey: Int) {
}, },
) )
} else { } else {
Switch( InnerDisableSwitch()
enabled = false,
checked = false,
onCheckedChange = {},
)
} }
} }
} }

View File

@ -13,11 +13,13 @@ import kotlinx.coroutines.flow.stateIn
import li.songe.gkd.data.ExcludeData import li.songe.gkd.data.ExcludeData
import li.songe.gkd.db.DbSet import li.songe.gkd.db.DbSet
import li.songe.gkd.util.SortTypeOption import li.songe.gkd.util.SortTypeOption
import li.songe.gkd.util.findOption
import li.songe.gkd.util.map import li.songe.gkd.util.map
import li.songe.gkd.util.orderedAppInfosFlow import li.songe.gkd.util.orderedAppInfosFlow
import li.songe.gkd.util.storeFlow
import li.songe.gkd.util.subsIdToRawFlow import li.songe.gkd.util.subsIdToRawFlow
class GlobalRuleExcludeVm (stateHandle: SavedStateHandle) : ViewModel() { class GlobalRuleExcludeVm(stateHandle: SavedStateHandle) : ViewModel() {
private val args = GlobalRuleExcludePageDestination.argsFrom(stateHandle) private val args = GlobalRuleExcludePageDestination.argsFrom(stateHandle)
val rawSubsFlow = subsIdToRawFlow.map(viewModelScope) { it[args.subsItemId] } val rawSubsFlow = subsIdToRawFlow.map(viewModelScope) { it[args.subsItemId] }
@ -40,9 +42,11 @@ class GlobalRuleExcludeVm (stateHandle: SavedStateHandle) : ViewModel() {
DbSet.clickLogDao.queryLatestUniqueAppIds(args.subsItemId, args.groupKey).map { appIds -> DbSet.clickLogDao.queryLatestUniqueAppIds(args.subsItemId, args.groupKey).map { appIds ->
appIds.mapIndexed { index, appId -> appId to index }.toMap() appIds.mapIndexed { index, appId -> appId to index }.toMap()
} }
val sortTypeFlow = MutableStateFlow<SortTypeOption>(SortTypeOption.SortByName) val sortTypeFlow = storeFlow.map(viewModelScope) {
val showSystemAppFlow = MutableStateFlow(true) SortTypeOption.allSubObject.findOption(it.subsExcludeSortType)
val showHiddenAppFlow = MutableStateFlow(false) }
val showSystemAppFlow = storeFlow.map(viewModelScope) { it.subsExcludeShowSystemApp }
val showHiddenAppFlow = storeFlow.map(viewModelScope) { it.subsExcludeShowHiddenApp }
val showAppInfosFlow = val showAppInfosFlow =
combine(orderedAppInfosFlow.combine(showHiddenAppFlow) { appInfos, showHiddenApp -> combine(orderedAppInfosFlow.combine(showHiddenAppFlow) { appInfos, showHiddenApp ->
if (showHiddenApp) { if (showHiddenApp) {

View File

@ -51,6 +51,7 @@ import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.annotation.RootGraph import com.ramcosta.composedestinations.annotation.RootGraph
import com.ramcosta.composedestinations.generated.destinations.AppItemPageDestination import com.ramcosta.composedestinations.generated.destinations.AppItemPageDestination
import com.ramcosta.composedestinations.utils.toDestinationsNavigator import com.ramcosta.composedestinations.utils.toDestinationsNavigator
import kotlinx.coroutines.flow.update
import li.songe.gkd.MainActivity import li.songe.gkd.MainActivity
import li.songe.gkd.data.RawSubscription import li.songe.gkd.data.RawSubscription
import li.songe.gkd.data.SubsConfig import li.songe.gkd.data.SubsConfig
@ -70,6 +71,8 @@ import li.songe.gkd.util.appInfoCacheFlow
import li.songe.gkd.util.json import li.songe.gkd.util.json
import li.songe.gkd.util.launchAsFn import li.songe.gkd.util.launchAsFn
import li.songe.gkd.util.launchTry import li.songe.gkd.util.launchTry
import li.songe.gkd.util.mapHashCode
import li.songe.gkd.util.storeFlow
import li.songe.gkd.util.throttle import li.songe.gkd.util.throttle
import li.songe.gkd.util.toast import li.songe.gkd.util.toast
import li.songe.gkd.util.updateSubscription import li.songe.gkd.util.updateSubscription
@ -117,16 +120,14 @@ fun SubsPage(
val showUninstallApp by vm.showUninstallAppFlow.collectAsState() val showUninstallApp by vm.showUninstallAppFlow.collectAsState()
val sortType by vm.sortTypeFlow.collectAsState() val sortType by vm.sortTypeFlow.collectAsState()
val listState = rememberLazyListState() val listState = rememberLazyListState()
var isFirstVisit by remember { mutableStateOf(false) } var isFirstVisit by remember { mutableStateOf(true) }
LaunchedEffect( LaunchedEffect(
appAndConfigs.size, key1 = appAndConfigs.mapHashCode { it.t0.id }
sortType.value,
appAndConfigs.fold(0) { acc, t -> 31 * acc + t.t0.id.hashCode() }
) { ) {
if (isFirstVisit) { if (isFirstVisit) {
listState.scrollToItem(0) isFirstVisit = false
} else { } else {
isFirstVisit = true listState.scrollToItem(0)
} }
} }
@ -197,11 +198,11 @@ fun SubsPage(
RadioButton( RadioButton(
selected = sortType == sortOption, selected = sortType == sortOption,
onClick = { onClick = {
vm.sortTypeFlow.value = sortOption storeFlow.update { s -> s.copy(subsAppSortType = sortOption.value) }
}) })
}, },
onClick = { onClick = {
vm.sortTypeFlow.value = sortOption storeFlow.update { s -> s.copy(subsAppSortType = sortOption.value) }
}, },
) )
} }
@ -217,11 +218,11 @@ fun SubsPage(
}, },
trailingIcon = { trailingIcon = {
Checkbox(checked = showUninstallApp, onCheckedChange = { Checkbox(checked = showUninstallApp, onCheckedChange = {
vm.showUninstallAppFlow.value = it storeFlow.update { s -> s.copy(subsAppShowUninstallApp = it) }
}) })
}, },
onClick = { onClick = {
vm.showUninstallAppFlow.value = !showUninstallApp storeFlow.update { s -> s.copy(subsAppShowUninstallApp = !showUninstallApp) }
}, },
) )
} }
@ -252,7 +253,8 @@ fun SubsPage(
subsConfig = subsConfig, subsConfig = subsConfig,
enableSize = enableSize, enableSize = enableSize,
onClick = throttle { onClick = throttle {
navController.toDestinationsNavigator().navigate(AppItemPageDestination(subsItemId, appRaw.id)) navController.toDestinationsNavigator()
.navigate(AppItemPageDestination(subsItemId, appRaw.id))
}, },
onValueChange = throttle(fn = vm.viewModelScope.launchAsFn { enable -> onValueChange = throttle(fn = vm.viewModelScope.launchAsFn { enable ->
val newItem = subsConfig?.copy( val newItem = subsConfig?.copy(
@ -284,11 +286,13 @@ fun SubsPage(
item { item {
Spacer(modifier = Modifier.height(EmptyHeight)) Spacer(modifier = Modifier.height(EmptyHeight))
if (appAndConfigs.isEmpty()) { if (appAndConfigs.isEmpty()) {
EmptyText(text = if (searchStr.isNotEmpty()) { EmptyText(
if (showUninstallApp) "暂无搜索结果" else "暂无搜索结果,请尝试修改筛选条件" text = if (searchStr.isNotEmpty()) {
} else { if (showUninstallApp) "暂无搜索结果" else "暂无搜索结果,请尝试修改筛选条件"
"暂无规则" } else {
}) "暂无规则"
}
)
} else if (editable) { } else if (editable) {
Spacer(modifier = Modifier.height(EmptyHeight)) Spacer(modifier = Modifier.height(EmptyHeight))
} }

View File

@ -17,12 +17,14 @@ import li.songe.gkd.db.DbSet
import li.songe.gkd.util.SortTypeOption import li.songe.gkd.util.SortTypeOption
import li.songe.gkd.util.appInfoCacheFlow import li.songe.gkd.util.appInfoCacheFlow
import li.songe.gkd.util.collator import li.songe.gkd.util.collator
import li.songe.gkd.util.findOption
import li.songe.gkd.util.getGroupRawEnable import li.songe.gkd.util.getGroupRawEnable
import li.songe.gkd.util.map import li.songe.gkd.util.map
import li.songe.gkd.util.storeFlow
import li.songe.gkd.util.subsIdToRawFlow import li.songe.gkd.util.subsIdToRawFlow
import li.songe.gkd.util.subsItemsFlow import li.songe.gkd.util.subsItemsFlow
class SubsVm (stateHandle: SavedStateHandle) : ViewModel() { class SubsVm(stateHandle: SavedStateHandle) : ViewModel() {
private val args = SubsPageDestination.argsFrom(stateHandle) private val args = SubsPageDestination.argsFrom(stateHandle)
val subsItemFlow = val subsItemFlow =
@ -43,9 +45,9 @@ class SubsVm (stateHandle: SavedStateHandle) : ViewModel() {
DbSet.clickLogDao.queryLatestUniqueAppIds(args.subsItemId).map { appIds -> DbSet.clickLogDao.queryLatestUniqueAppIds(args.subsItemId).map { appIds ->
appIds.mapIndexed { index, appId -> appId to index }.toMap() appIds.mapIndexed { index, appId -> appId to index }.toMap()
} }
val sortTypeFlow = MutableStateFlow<SortTypeOption>(SortTypeOption.SortByName) val sortTypeFlow = storeFlow.map(viewModelScope) { SortTypeOption.allSubObject.findOption(it.subsAppSortType) }
val showUninstallAppFlow = MutableStateFlow(false) val showUninstallAppFlow = storeFlow.map(viewModelScope) { it.subsAppShowUninstallApp }
private val sortAppsFlow = private val sortAppsFlow =
combine(combine((subsRawFlow.combine(appInfoCacheFlow) { subs, appInfoCache -> combine(combine((subsRawFlow.combine(appInfoCacheFlow) { subs, appInfoCache ->
(subs?.apps ?: emptyList()).sortedWith { a, b -> (subs?.apps ?: emptyList()).sortedWith { a, b ->

View File

@ -69,6 +69,7 @@ import li.songe.gkd.ui.style.appItemPadding
import li.songe.gkd.ui.style.menuPadding import li.songe.gkd.ui.style.menuPadding
import li.songe.gkd.util.LocalNavController import li.songe.gkd.util.LocalNavController
import li.songe.gkd.util.SortTypeOption import li.songe.gkd.util.SortTypeOption
import li.songe.gkd.util.mapHashCode
import li.songe.gkd.util.ruleSummaryFlow import li.songe.gkd.util.ruleSummaryFlow
import li.songe.gkd.util.storeFlow import li.songe.gkd.util.storeFlow
import li.songe.gkd.util.throttle import li.songe.gkd.util.throttle
@ -112,14 +113,16 @@ fun useAppListPage(): ScaffoldExt {
}) })
val listState = rememberLazyListState() val listState = rememberLazyListState()
var isFirstVisit by remember { mutableStateOf(false) } var isFirstVisit by remember { mutableStateOf(true) }
LaunchedEffect(key1 = orderedAppInfos, block = { LaunchedEffect(
key1 = orderedAppInfos.mapHashCode { it.id }
) {
if (isFirstVisit) { if (isFirstVisit) {
listState.scrollToItem(0) isFirstVisit = false
} else { } else {
isFirstVisit = true listState.scrollToItem(0)
} }
}) }
val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior() val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior()
return ScaffoldExt( return ScaffoldExt(
navItem = appListNav, navItem = appListNav,
@ -265,7 +268,9 @@ fun useAppListPage(): ScaffoldExt {
Row( Row(
modifier = Modifier modifier = Modifier
.clickable(onClick = throttle { .clickable(onClick = throttle {
navController.toDestinationsNavigator().navigate(AppConfigPageDestination(appInfo.id)) navController
.toDestinationsNavigator()
.navigate(AppConfigPageDestination(appInfo.id))
}) })
.height(IntrinsicSize.Min) .height(IntrinsicSize.Min)
.appItemPadding(), .appItemPadding(),

View File

@ -0,0 +1,5 @@
package li.songe.gkd.util
inline fun <T, R> Iterable<T>.mapHashCode(transform: (T) -> R): Int {
return fold(0) { acc, t -> 31 * acc + transform(t).hashCode() }
}

View File

@ -74,15 +74,21 @@ data class Store(
val enableDarkTheme: Boolean? = null, val enableDarkTheme: Boolean? = null,
val enableDynamicColor: Boolean = true, val enableDynamicColor: Boolean = true,
val enableAbFloatWindow: Boolean = true, val enableAbFloatWindow: Boolean = true,
val sortType: Int = SortTypeOption.SortByName.value,
val showSystemApp: Boolean = true,
val showHiddenApp: Boolean = false,
val showSaveSnapshotToast: Boolean = true, val showSaveSnapshotToast: Boolean = true,
val useSystemToast: Boolean = false, val useSystemToast: Boolean = false,
val useCustomNotifText: Boolean = false, val useCustomNotifText: Boolean = false,
val customNotifText: String = "\${i}全局/\${k}应用/\${u}规则组/\${n}触发", val customNotifText: String = "\${i}全局/\${k}应用/\${u}规则组/\${n}触发",
val enableActivityLog: Boolean = false, val enableActivityLog: Boolean = false,
val updateChannel: Int = if (META.versionName.contains("beta")) UpdateChannelOption.Beta.value else UpdateChannelOption.Stable.value, val updateChannel: Int = if (META.versionName.contains("beta")) UpdateChannelOption.Beta.value else UpdateChannelOption.Stable.value,
val sortType: Int = SortTypeOption.SortByName.value,
val showSystemApp: Boolean = true,
val showHiddenApp: Boolean = false,
val appRuleSortType: Int = RuleSortOption.Default.value,
val subsAppSortType: Int = SortTypeOption.SortByName.value,
val subsAppShowUninstallApp: Boolean = false,
val subsExcludeSortType: Int = SortTypeOption.SortByName.value,
val subsExcludeShowSystemApp: Boolean = true,
val subsExcludeShowHiddenApp: Boolean = false,
) )
val storeFlow by lazy { val storeFlow by lazy {