Skip to content

Commit

Permalink
perf: sort
Browse files Browse the repository at this point in the history
  • Loading branch information
lisonge committed Feb 5, 2024
1 parent b753054 commit a34779e
Show file tree
Hide file tree
Showing 11 changed files with 191 additions and 175 deletions.
9 changes: 9 additions & 0 deletions app/src/main/kotlin/li/songe/gkd/data/ClickLog.kt
Original file line number Diff line number Diff line change
Expand Up @@ -73,5 +73,14 @@ data class ClickLog(
"""
)
suspend fun deleteKeepLatest(): Int

@Query("SELECT DISTINCT app_id FROM click_log ORDER BY id DESC")
fun queryLatestUniqueAppIds(): Flow<List<String>>

@Query("SELECT DISTINCT app_id FROM click_log WHERE subs_id=:subsItemId AND group_type=${SubsConfig.AppGroupType} ORDER BY id DESC")
fun queryLatestUniqueAppIds(subsItemId: Long): Flow<List<String>>

@Query("SELECT DISTINCT app_id FROM click_log WHERE subs_id=:subsItemId AND group_key=:globalGroupKey AND group_type=${SubsConfig.GlobalGroupType} ORDER BY id DESC")
fun queryLatestUniqueAppIds(subsItemId: Long, globalGroupKey: Int): Flow<List<String>>
}
}
7 changes: 4 additions & 3 deletions app/src/main/kotlin/li/songe/gkd/ui/ClickLogPage.kt
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ fun ClickLogPage() {
)
}
},
title = { Text(text = "点击记录" + if (clickLogCount <= 0) "" else ("-$clickLogCount")) },
title = { Text(text = "触发记录" + if (clickLogCount <= 0) "" else ("-$clickLogCount")) },
actions = {
if (clickDataList.isNotEmpty()) {
IconButton(onClick = { showDeleteDlg = true }) {
Expand Down Expand Up @@ -336,8 +336,9 @@ fun ClickLogPage() {
}

if (showDeleteDlg) {
AlertDialog(onDismissRequest = { showDeleteDlg = false },
title = { Text(text = "是否删除全部点击记录?") },
AlertDialog(
onDismissRequest = { showDeleteDlg = false },
title = { Text(text = "是否删除全部点击触发记录?") },
confirmButton = {
TextButton(onClick = scope.launchAsFn(Dispatchers.IO) {
showDeleteDlg = false
Expand Down
53 changes: 17 additions & 36 deletions app/src/main/kotlin/li/songe/gkd/ui/GlobalRuleExcludePage.kt
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ import li.songe.gkd.service.launcherAppId
import li.songe.gkd.ui.component.AppBarTextField
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.toast

Expand All @@ -86,7 +87,7 @@ fun GlobalRuleExcludePage(subsItemId: Long, groupKey: Int) {
val showAppInfos = vm.showAppInfosFlow.collectAsState().value
val searchStr by vm.searchStrFlow.collectAsState()
val showSystemApp by vm.showSystemAppFlow.collectAsState()
val sortByMtime by vm.sortByMtimeFlow.collectAsState()
val sortType by vm.sortTypeFlow.collectAsState()

var showEditDlg by remember {
mutableStateOf(false)
Expand All @@ -103,9 +104,7 @@ fun GlobalRuleExcludePage(subsItemId: Long, groupKey: Int) {
val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior()
val listState = rememberLazyListState()
LaunchedEffect(key1 = showAppInfos, block = {
if (showAppInfos.isNotEmpty()) {
listState.animateScrollToItem(0)
}
listState.animateScrollToItem(0)
})
var expanded by remember { mutableStateOf(false) }
Scaffold(modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection), topBar = {
Expand Down Expand Up @@ -173,43 +172,25 @@ fun GlobalRuleExcludePage(subsItemId: Long, groupKey: Int) {
expanded = expanded,
onDismissRequest = { expanded = false }
) {
DropdownMenuItem(
text = {
Row(
verticalAlignment = Alignment.CenterVertically
) {
RadioButton(selected = !sortByMtime,
onClick = {
vm.sortByMtimeFlow.value = false
}
)
Text("按名称")
}
},
onClick = {
vm.sortByMtimeFlow.value = false
},
)
DropdownMenuItem(
text = {
Row(
verticalAlignment = Alignment.CenterVertically
) {
SortTypeOption.allSubObject.forEach { sortOption ->
DropdownMenuItem(
text = {
Row(
verticalAlignment = Alignment.CenterVertically
) {
RadioButton(
selected = sortByMtime,
onClick = { vm.sortByMtimeFlow.value = true }
RadioButton(selected = sortType == sortOption,
onClick = {
vm.sortTypeFlow.value = sortOption
}
)
Text("按更新时间")
Text(sortOption.label)
}
}
},
onClick = {
vm.sortByMtimeFlow.value = true
},
)
},
onClick = {
vm.sortTypeFlow.value = sortOption
},
)
}
HorizontalDivider()
DropdownMenuItem(
text = {
Expand Down
50 changes: 29 additions & 21 deletions app/src/main/kotlin/li/songe/gkd/ui/GlobalRuleExcludeVm.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import kotlinx.coroutines.flow.stateIn
import li.songe.gkd.data.ExcludeData
import li.songe.gkd.db.DbSet
import li.songe.gkd.ui.destinations.GlobalRuleExcludePageDestination
import li.songe.gkd.util.SortTypeOption
import li.songe.gkd.util.map
import li.songe.gkd.util.orderedAppInfosFlow
import li.songe.gkd.util.subsIdToRawFlow
Expand All @@ -38,32 +39,39 @@ class GlobalRuleExcludeVm @Inject constructor(stateHandle: SavedStateHandle) : V
private val debounceSearchStrFlow = searchStrFlow.debounce(200)
.stateIn(viewModelScope, SharingStarted.Eagerly, searchStrFlow.value)

val sortByMtimeFlow = MutableStateFlow(false)
private val appIdToOrderFlow =
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(false)
val showAppInfosFlow = combine(
debounceSearchStrFlow,
orderedAppInfosFlow,
sortByMtimeFlow,
showSystemAppFlow
) { str, list, sortByMtime, showSystemApp ->
list.let {
if (sortByMtime) {
it.sortedBy { a -> -a.mtime }
val showAppInfosFlow =
combine(orderedAppInfosFlow.combine(showSystemAppFlow) { apps, showSystemApp ->
if (showSystemApp) {
apps
} else {
it
apps.filter { a -> !a.isSystem }
}
}.let {
if (!showSystemApp) {
it.filter { a -> !a.isSystem }
} else {
it
}, sortTypeFlow, appIdToOrderFlow) { apps, sortType, appIdToOrder ->
when (sortType) {
SortTypeOption.SortByAppMtime -> {
apps.sortedBy { a -> -a.mtime }
}

SortTypeOption.SortByTriggerTime -> {
apps.sortedBy { a -> appIdToOrder[a.id] ?: Int.MAX_VALUE }
}

SortTypeOption.SortByName -> {
apps
}
}
}.let {
}.combine(debounceSearchStrFlow) { apps, str ->
if (str.isBlank()) {
it
apps
} else {
(it.filter { a -> a.name.contains(str) } + it.filter { a -> a.id.contains(str) }).distinct()
(apps.filter { a -> a.name.contains(str) } + apps.filter { a -> a.id.contains(str) }).distinct()
}
}
}.stateIn(viewModelScope, SharingStarted.Eagerly, emptyList())
}.stateIn(viewModelScope, SharingStarted.Eagerly, emptyList())

}
59 changes: 25 additions & 34 deletions app/src/main/kotlin/li/songe/gkd/ui/SubsPage.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package li.songe.gkd.ui

import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
Expand All @@ -12,6 +11,7 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
Expand Down Expand Up @@ -68,6 +68,7 @@ import li.songe.gkd.ui.component.SubsAppCard
import li.songe.gkd.ui.destinations.AppItemPageDestination
import li.songe.gkd.util.LocalNavController
import li.songe.gkd.util.ProfileTransitions
import li.songe.gkd.util.SortTypeOption
import li.songe.gkd.util.appInfoCacheFlow
import li.songe.gkd.util.encodeToJson5String
import li.songe.gkd.util.json
Expand Down Expand Up @@ -121,7 +122,11 @@ fun SubsPage(
val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior()
var expanded by remember { mutableStateOf(false) }
val showUninstallApp by vm.showUninstallAppFlow.collectAsState()
val sortByMtime by vm.sortByMtimeFlow.collectAsState()
val sortType by vm.sortTypeFlow.collectAsState()
val listState = rememberLazyListState()
LaunchedEffect(key1 = appAndConfigs, block = {
listState.scrollToItem(0)
})

Scaffold(
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
Expand Down Expand Up @@ -182,41 +187,26 @@ fun SubsPage(
expanded = expanded,
onDismissRequest = { expanded = false }
) {
DropdownMenuItem(
text = {
Row(
verticalAlignment = Alignment.CenterVertically
) {
RadioButton(selected = !sortByMtime,
onClick = { vm.sortByMtimeFlow.value = false }
)
Text("按名称")
}
},
onClick = {
vm.sortByMtimeFlow.value = false
},
)
DropdownMenuItem(
text = {
Row(
verticalAlignment = Alignment.CenterVertically
) {

SortTypeOption.allSubObject.forEach { sortOption ->
DropdownMenuItem(
text = {
Row(
verticalAlignment = Alignment.CenterVertically
) {
RadioButton(
selected = sortByMtime,
onClick = { vm.sortByMtimeFlow.value = true }
RadioButton(selected = sortType == sortOption,
onClick = {
vm.sortTypeFlow.value = sortOption
}
)
Text("按更新时间")
Text(sortOption.label)
}
}
},
onClick = {
vm.sortByMtimeFlow.value = true
},
)
},
onClick = {
vm.sortTypeFlow.value = sortOption
},
)
}
HorizontalDivider()
DropdownMenuItem(
text = {
Expand Down Expand Up @@ -246,14 +236,15 @@ fun SubsPage(
FloatingActionButton(onClick = { showAddDlg = true }) {
Icon(
imageVector = Icons.Filled.Add,
contentDescription = "add",
contentDescription = null,
)
}
}
},
) { padding ->
LazyColumn(
verticalArrangement = Arrangement.spacedBy(0.dp), modifier = Modifier.padding(padding)
modifier = Modifier.padding(padding),
state = listState
) {
itemsIndexed(appAndConfigs, { i, a -> i.toString() + a.t0.id }) { _, a ->
val (appRaw, subsConfig, enableSize) = a
Expand Down
60 changes: 38 additions & 22 deletions app/src/main/kotlin/li/songe/gkd/ui/SubsVm.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import li.songe.gkd.data.RawSubscription
import li.songe.gkd.data.SubsConfig
import li.songe.gkd.data.Tuple3
import li.songe.gkd.db.DbSet
import li.songe.gkd.ui.destinations.SubsPageDestination
import li.songe.gkd.util.SortTypeOption
import li.songe.gkd.util.appInfoCacheFlow
import li.songe.gkd.util.collator
import li.songe.gkd.util.getGroupRawEnable
Expand All @@ -40,33 +42,47 @@ class SubsVm @Inject constructor(stateHandle: SavedStateHandle) : ViewModel() {
private val categoryConfigsFlow = DbSet.categoryConfigDao.queryConfig(args.subsItemId)
.stateIn(viewModelScope, SharingStarted.Eagerly, emptyList())

val sortByMtimeFlow = MutableStateFlow(false)
private val appIdToOrderFlow =
DbSet.clickLogDao.queryLatestUniqueAppIds(args.subsItemId).map { appIds ->
appIds.mapIndexed { index, appId -> appId to index }.toMap()
}
val sortTypeFlow = MutableStateFlow<SortTypeOption>(SortTypeOption.SortByName)

val showUninstallAppFlow = MutableStateFlow(false)
private val sortAppsFlow = combine(
subsRawFlow, appInfoCacheFlow, showUninstallAppFlow, sortByMtimeFlow
) { subsRaw, appInfoCache, showUninstallApp, sortByMtime ->
val apps = (subsRaw?.apps ?: emptyList()).let {
if (showUninstallApp) {
it
} else {
it.filter { a -> appInfoCache.containsKey(a.id) }
private val sortAppsFlow =
combine(combine((subsRawFlow.combine(appInfoCacheFlow) { subs, appInfoCache ->
(subs?.apps ?: emptyList()).sortedWith { a, b ->
// 顺序: 已安装(有名字->无名字)->未安装(有名字(来自订阅)->无名字)
collator.compare(appInfoCache[a.id]?.name ?: a.name?.let { "\uFFFF" + it }
?: ("\uFFFF\uFFFF" + a.id),
appInfoCache[b.id]?.name ?: b.name?.let { "\uFFFF" + it }
?: ("\uFFFF\uFFFF" + b.id))
}
}
apps.sortedWith { a, b ->
// 顺序: 已安装(有名字->无名字)->未安装(有名字(来自订阅)->无名字)
collator.compare(appInfoCache[a.id]?.name ?: a.name?.let { "\uFFFF" + it }
?: ("\uFFFF\uFFFF" + a.id),
appInfoCache[b.id]?.name ?: b.name?.let { "\uFFFF" + it }
?: ("\uFFFF\uFFFF" + b.id))
}.let {
if (sortByMtime) {
it.sortedBy { a -> -(appInfoCache[a.id]?.mtime ?: 0) }
}), appInfoCacheFlow, showUninstallAppFlow) { apps, appInfoCache, showUninstallApp ->
if (showUninstallApp) {
apps
} else {
it
apps.filter { a -> appInfoCache.containsKey(a.id) }
}
}
}.stateIn(viewModelScope, SharingStarted.Eagerly, emptyList())
},
appInfoCacheFlow,
appIdToOrderFlow,
sortTypeFlow
) { apps, appInfoCache, appIdToOrder, sortType ->
when (sortType) {
SortTypeOption.SortByAppMtime -> {
apps.sortedBy { a -> -(appInfoCache[a.id]?.mtime ?: 0) }
}

SortTypeOption.SortByTriggerTime -> {
apps.sortedBy { a -> appIdToOrder[a.id] ?: Int.MAX_VALUE }
}

SortTypeOption.SortByName -> {
apps
}
}
}.stateIn(viewModelScope, SharingStarted.Eagerly, emptyList())

val searchStrFlow = MutableStateFlow("")

Expand Down
Loading

0 comments on commit a34779e

Please sign in to comment.