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.appInfoCacheFlow
import li.songe.gkd.util.launchTry
import li.songe.gkd.util.storeFlow
import li.songe.gkd.util.throttle
@Destination<RootGraph>(style = ProfileTransitions::class)
@ -148,12 +149,12 @@ fun AppConfigPage(appId: String) {
RadioButton(
selected = ruleSortType == s,
onClick = {
vm.ruleSortTypeFlow.update { s }
storeFlow.update { it.copy(appRuleSortType = s.value) }
}
)
},
onClick = {
vm.ruleSortTypeFlow.update { s }
storeFlow.update { it.copy(appRuleSortType = s.value) }
},
)
}
@ -164,7 +165,8 @@ fun AppConfigPage(appId: String) {
floatingActionButton = {
FloatingActionButton(
onClick = throttle {
navController.toDestinationsNavigator().navigate(AppItemPageDestination(LOCAL_SUBS_ID, appId))
navController.toDestinationsNavigator()
.navigate(AppItemPageDestination(LOCAL_SUBS_ID, appId))
},
content = {
Icon(
@ -295,7 +297,6 @@ private fun AppGroupCard(
onClick: () -> Unit,
onCheckedChange: ((Boolean) -> Unit)?,
) {
val context = LocalContext.current as MainActivity
Row(
modifier = Modifier
.clickable(onClick = onClick)
@ -349,6 +350,14 @@ private fun AppGroupCard(
if (checked != null) {
Switch(checked = checked, onCheckedChange = onCheckedChange)
} else {
InnerDisableSwitch()
}
}
}
@Composable
fun InnerDisableSwitch() {
val context = LocalContext.current as MainActivity
Switch(
checked = false,
enabled = false,
@ -363,6 +372,4 @@ private fun AppGroupCard(
)
}
)
}
}
}

View File

@ -4,7 +4,6 @@ import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.ramcosta.composedestinations.generated.destinations.AppConfigPageDestination
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.combine
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.RuleSortOption
import li.songe.gkd.util.collator
import li.songe.gkd.util.findOption
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.subsItemsFlow
class AppConfigVm (stateHandle: SavedStateHandle) : ViewModel() {
class AppConfigVm(stateHandle: SavedStateHandle) : ViewModel() {
private val args = AppConfigPageDestination.argsFrom(stateHandle)
private val latestGlobalLogsFlow = DbSet.clickLogDao.queryAppLatest(
@ -33,7 +35,8 @@ class AppConfigVm (stateHandle: SavedStateHandle) : ViewModel() {
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 ->
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() {
if (shizukuOkState.stateFlow.value) {

View File

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

View File

@ -13,11 +13,13 @@ import kotlinx.coroutines.flow.stateIn
import li.songe.gkd.data.ExcludeData
import li.songe.gkd.db.DbSet
import li.songe.gkd.util.SortTypeOption
import li.songe.gkd.util.findOption
import li.songe.gkd.util.map
import li.songe.gkd.util.orderedAppInfosFlow
import li.songe.gkd.util.storeFlow
import li.songe.gkd.util.subsIdToRawFlow
class GlobalRuleExcludeVm (stateHandle: SavedStateHandle) : ViewModel() {
class GlobalRuleExcludeVm(stateHandle: SavedStateHandle) : ViewModel() {
private val args = GlobalRuleExcludePageDestination.argsFrom(stateHandle)
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 ->
appIds.mapIndexed { index, appId -> appId to index }.toMap()
}
val sortTypeFlow = MutableStateFlow<SortTypeOption>(SortTypeOption.SortByName)
val showSystemAppFlow = MutableStateFlow(true)
val showHiddenAppFlow = MutableStateFlow(false)
val sortTypeFlow = storeFlow.map(viewModelScope) {
SortTypeOption.allSubObject.findOption(it.subsExcludeSortType)
}
val showSystemAppFlow = storeFlow.map(viewModelScope) { it.subsExcludeShowSystemApp }
val showHiddenAppFlow = storeFlow.map(viewModelScope) { it.subsExcludeShowHiddenApp }
val showAppInfosFlow =
combine(orderedAppInfosFlow.combine(showHiddenAppFlow) { appInfos, 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.generated.destinations.AppItemPageDestination
import com.ramcosta.composedestinations.utils.toDestinationsNavigator
import kotlinx.coroutines.flow.update
import li.songe.gkd.MainActivity
import li.songe.gkd.data.RawSubscription
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.launchAsFn
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.toast
import li.songe.gkd.util.updateSubscription
@ -117,16 +120,14 @@ fun SubsPage(
val showUninstallApp by vm.showUninstallAppFlow.collectAsState()
val sortType by vm.sortTypeFlow.collectAsState()
val listState = rememberLazyListState()
var isFirstVisit by remember { mutableStateOf(false) }
var isFirstVisit by remember { mutableStateOf(true) }
LaunchedEffect(
appAndConfigs.size,
sortType.value,
appAndConfigs.fold(0) { acc, t -> 31 * acc + t.t0.id.hashCode() }
key1 = appAndConfigs.mapHashCode { it.t0.id }
) {
if (isFirstVisit) {
listState.scrollToItem(0)
isFirstVisit = false
} else {
isFirstVisit = true
listState.scrollToItem(0)
}
}
@ -197,11 +198,11 @@ fun SubsPage(
RadioButton(
selected = sortType == sortOption,
onClick = {
vm.sortTypeFlow.value = sortOption
storeFlow.update { s -> s.copy(subsAppSortType = sortOption.value) }
})
},
onClick = {
vm.sortTypeFlow.value = sortOption
storeFlow.update { s -> s.copy(subsAppSortType = sortOption.value) }
},
)
}
@ -217,11 +218,11 @@ fun SubsPage(
},
trailingIcon = {
Checkbox(checked = showUninstallApp, onCheckedChange = {
vm.showUninstallAppFlow.value = it
storeFlow.update { s -> s.copy(subsAppShowUninstallApp = it) }
})
},
onClick = {
vm.showUninstallAppFlow.value = !showUninstallApp
storeFlow.update { s -> s.copy(subsAppShowUninstallApp = !showUninstallApp) }
},
)
}
@ -252,7 +253,8 @@ fun SubsPage(
subsConfig = subsConfig,
enableSize = enableSize,
onClick = throttle {
navController.toDestinationsNavigator().navigate(AppItemPageDestination(subsItemId, appRaw.id))
navController.toDestinationsNavigator()
.navigate(AppItemPageDestination(subsItemId, appRaw.id))
},
onValueChange = throttle(fn = vm.viewModelScope.launchAsFn { enable ->
val newItem = subsConfig?.copy(
@ -284,11 +286,13 @@ fun SubsPage(
item {
Spacer(modifier = Modifier.height(EmptyHeight))
if (appAndConfigs.isEmpty()) {
EmptyText(text = if (searchStr.isNotEmpty()) {
EmptyText(
text = if (searchStr.isNotEmpty()) {
if (showUninstallApp) "暂无搜索结果" else "暂无搜索结果,请尝试修改筛选条件"
} else {
"暂无规则"
})
}
)
} else if (editable) {
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.appInfoCacheFlow
import li.songe.gkd.util.collator
import li.songe.gkd.util.findOption
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.subsItemsFlow
class SubsVm (stateHandle: SavedStateHandle) : ViewModel() {
class SubsVm(stateHandle: SavedStateHandle) : ViewModel() {
private val args = SubsPageDestination.argsFrom(stateHandle)
val subsItemFlow =
@ -43,9 +45,9 @@ class SubsVm (stateHandle: SavedStateHandle) : ViewModel() {
DbSet.clickLogDao.queryLatestUniqueAppIds(args.subsItemId).map { appIds ->
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 =
combine(combine((subsRawFlow.combine(appInfoCacheFlow) { subs, appInfoCache ->
(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.util.LocalNavController
import li.songe.gkd.util.SortTypeOption
import li.songe.gkd.util.mapHashCode
import li.songe.gkd.util.ruleSummaryFlow
import li.songe.gkd.util.storeFlow
import li.songe.gkd.util.throttle
@ -112,14 +113,16 @@ fun useAppListPage(): ScaffoldExt {
})
val listState = rememberLazyListState()
var isFirstVisit by remember { mutableStateOf(false) }
LaunchedEffect(key1 = orderedAppInfos, block = {
var isFirstVisit by remember { mutableStateOf(true) }
LaunchedEffect(
key1 = orderedAppInfos.mapHashCode { it.id }
) {
if (isFirstVisit) {
listState.scrollToItem(0)
isFirstVisit = false
} else {
isFirstVisit = true
listState.scrollToItem(0)
}
}
})
val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior()
return ScaffoldExt(
navItem = appListNav,
@ -265,7 +268,9 @@ fun useAppListPage(): ScaffoldExt {
Row(
modifier = Modifier
.clickable(onClick = throttle {
navController.toDestinationsNavigator().navigate(AppConfigPageDestination(appInfo.id))
navController
.toDestinationsNavigator()
.navigate(AppConfigPageDestination(appInfo.id))
})
.height(IntrinsicSize.Min)
.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 enableDynamicColor: 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 useSystemToast: Boolean = false,
val useCustomNotifText: Boolean = false,
val customNotifText: String = "\${i}全局/\${k}应用/\${u}规则组/\${n}触发",
val enableActivityLog: Boolean = false,
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 {