Skip to content

Commit 65cedcc

Browse files
committed
Add: Tags' filter function (closes #968)
1 parent e0751ed commit 65cedcc

File tree

5 files changed

+118
-29
lines changed

5 files changed

+118
-29
lines changed

src/main/java/com/machiav3lli/backup/data/entity/States.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,5 @@ data class MainState(
77
val blocklist: Set<String> = emptySet(),
88
val searchQuery: String = "",
99
val sortFilter: SortFilterModel = SortFilterModel(),
10-
val selection: Set<String> = emptySet()
10+
val selection: Set<String> = emptySet(),
1111
)

src/main/java/com/machiav3lli/backup/ui/pages/ToolsPrefsPage.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -437,6 +437,7 @@ private fun Context.onClickSaveAppsList(
437437
showDialog: (() -> Unit, () -> Unit) -> Unit
438438
): Boolean {
439439
val packageList = viewModel.packageMap.value.values
440+
val tagsMap = viewModel.tagsMap.value
440441
if (packageList.isNotEmpty()) {
441442
showDialog(
442443
{
@@ -449,7 +450,7 @@ private fun Context.onClickSaveAppsList(
449450
},
450451
{
451452
writeAppsListFile( // TODO communicate that the filter from home page is used
452-
packageList.applyFilter(viewModel.homeState.value.sortFilter)
453+
packageList.applyFilter(viewModel.homeState.value.sortFilter, tagsMap)
453454
.map { "${it.packageLabel}: ${it.packageName} @ ${it.versionName}" },
454455
true
455456
)

src/main/java/com/machiav3lli/backup/ui/sheets/SortFilterSheet.kt

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,14 +106,16 @@ fun SortFilterSheet(
106106
)
107107
}
108108
val stateModel by state.collectAsState()
109+
val tagsMap by viewModel.tagsMap.collectAsState()
110+
val allTags by viewModel.allTags.collectAsState()
109111
var model by rememberSaveable(stateModel.sortFilter) {
110112
mutableStateOf(state.value.sortFilter)
111113
}
112114

113115
val stats = getStats(
114116
stateModel.packages
115117
.filterNot { it.packageName in stateModel.blocklist }
116-
.applyFilter(model)
118+
.applyFilter(model, tagsMap)
117119
) //TODO hg42 use central function for all the filtering
118120

119121
Scaffold(
@@ -352,6 +354,19 @@ fun SortFilterSheet(
352354
}
353355
}
354356
}
357+
if (allTags.isNotEmpty()) item {
358+
ExpandableBlock(
359+
heading = stringResource(id = R.string.filters_tags),
360+
preExpanded = model.tags.isNotEmpty(),
361+
) {
362+
MultiSelectableChipGroup(
363+
list = allTags.toSet(),
364+
selected = model.tags,
365+
) { tags ->
366+
model = model.copy(tags = tags.intersect(allTags))
367+
}
368+
}
369+
}
355370
}
356371
}
357372
}

src/main/java/com/machiav3lli/backup/utils/FilterUtils.kt

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -93,27 +93,26 @@ fun filterPackages(
9393

9494
//---------------------------------------- filters for activity
9595

96-
fun Collection<Package>.applySearchAndFilter(
96+
fun Collection<Package>.applySearch(
9797
query: String,
9898
extras: Map<String, AppExtras>,
99-
filter: SortFilterModel,
100-
): List<Package> {
101-
return filter { item ->
102-
query.isEmpty() || (
103-
(extras[item.packageName]?.customTags ?: emptySet()).plus(
104-
listOfNotNull(
105-
item.packageName,
106-
item.packageLabel,
107-
extras[item.packageName]?.note
108-
)
109-
)
110-
.any { it.contains(query, ignoreCase = true) }
99+
): List<Package> = filter { item ->
100+
query.isEmpty() || (
101+
(extras[item.packageName]?.customTags ?: emptySet()).plus(
102+
listOfNotNull(
103+
item.packageName,
104+
item.packageLabel,
105+
extras[item.packageName]?.note
111106
)
112-
}
113-
.applyFilter(filter)
107+
)
108+
.any { it.contains(query, ignoreCase = true) }
109+
)
114110
}
115111

116-
fun Collection<Package>.applyFilter(filter: SortFilterModel): List<Package> {
112+
fun Collection<Package>.applyFilter(
113+
filter: SortFilterModel,
114+
tagsMap: Map<String, Set<String>>,
115+
): List<Package> {
117116
val predicate: (Package) -> Boolean = {
118117
(if (filter.mainFilter and MAIN_FILTER_SYSTEM == MAIN_FILTER_SYSTEM) it.isSystem && !it.isSpecial else false)
119118
|| (if (filter.mainFilter and MAIN_FILTER_USER == MAIN_FILTER_USER) !it.isSystem else false)
@@ -123,6 +122,7 @@ fun Collection<Package>.applyFilter(filter: SortFilterModel): List<Package> {
123122
.applyBackupFilter(filter.backupFilter)
124123
.applySpecialFilter(filter.specialFilter)
125124
.applySort(filter.sort, filter.sortAsc)
125+
.applyTagsFilter(filter.tags, tagsMap)
126126
}
127127

128128
private fun Collection<Package>.applyBackupFilter(backupFilter: Int): List<Package> {
@@ -268,6 +268,13 @@ private fun List<Package>.applySort(sort: Int, sortAsc: Boolean): List<Package>
268268
}
269269
}
270270

271+
fun List<Package>.applyTagsFilter(
272+
tags: Set<String>,
273+
tagsMap: Map<String, Set<String>>
274+
): List<Package> = filter {
275+
tags.isEmpty() || (tagsMap[it.packageName] ?: emptySet()).any { tag -> tag in tags }
276+
}
277+
271278
fun filterToString(context: Context, filter: Int): String = possibleMainFilters
272279
.filter { it and filter == it }
273280
.let { activeFilters ->

src/main/java/com/machiav3lli/backup/viewmodels/MainVM.kt

Lines changed: 76 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ import androidx.lifecycle.viewModelScope
2121
import com.machiav3lli.backup.MAIN_FILTER_DEFAULT
2222
import com.machiav3lli.backup.MAIN_FILTER_DEFAULT_WITHOUT_SPECIAL
2323
import com.machiav3lli.backup.STATEFLOW_SUBSCRIBE_BUFFER
24+
import com.machiav3lli.backup.data.dbs.entity.AppExtras
25+
import com.machiav3lli.backup.data.dbs.repository.AppExtrasRepository
2426
import com.machiav3lli.backup.data.dbs.repository.BlocklistRepository
2527
import com.machiav3lli.backup.data.dbs.repository.PackageRepository
2628
import com.machiav3lli.backup.data.entity.MainState
@@ -31,7 +33,8 @@ import com.machiav3lli.backup.data.preferences.traceFlows
3133
import com.machiav3lli.backup.ui.navigation.NavItem
3234
import com.machiav3lli.backup.ui.pages.pref_newAndUpdatedNotification
3335
import com.machiav3lli.backup.utils.TraceUtils.trace
34-
import com.machiav3lli.backup.utils.applySearchAndFilter
36+
import com.machiav3lli.backup.utils.applyFilter
37+
import com.machiav3lli.backup.utils.applySearch
3538
import com.machiav3lli.backup.utils.extensions.IconCache
3639
import com.machiav3lli.backup.utils.extensions.NeoViewModel
3740
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -48,6 +51,7 @@ import kotlinx.coroutines.launch
4851
class MainVM(
4952
private val packageRepository: PackageRepository,
5053
private val blocklistRepository: BlocklistRepository,
54+
appExtrasRepository: AppExtrasRepository,
5155
private val prefs: NeoPrefs,
5256
) : NeoViewModel() {
5357
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - FLOWS
@@ -92,6 +96,38 @@ class MainVM(
9296
emptyMap()
9397
)
9498

99+
private val notBlockedPackages = combine(
100+
packageRepository.getPackagesFlow(),
101+
blocklistRepository.getBlocklist(),
102+
) { packages, blocklist ->
103+
packages.filterNot { it.packageName in blocklist }
104+
}
105+
106+
private val extras = appExtrasRepository.getAllFlow()
107+
.mapLatest { it.associateBy { extra -> extra.packageName } }
108+
.stateIn(
109+
viewModelScope,
110+
started = SharingStarted.WhileSubscribed(STATEFLOW_SUBSCRIBE_BUFFER),
111+
emptyMap()
112+
)
113+
114+
val tagsMap = extras
115+
.mapLatest { it.mapValues { extras -> extras.value.customTags } }
116+
.stateIn(
117+
viewModelScope,
118+
started = SharingStarted.WhileSubscribed(STATEFLOW_SUBSCRIBE_BUFFER),
119+
emptyMap()
120+
)
121+
122+
val allTags = tagsMap
123+
.mapLatest { it.values.flatten().toSet() }
124+
.trace { "*** all tags <<- ${it.size}" }
125+
.stateIn(
126+
viewModelScope,
127+
started = SharingStarted.WhileSubscribed(STATEFLOW_SUBSCRIBE_BUFFER),
128+
emptySet()
129+
)
130+
95131
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - FLOWS end
96132

97133
init {
@@ -104,14 +140,23 @@ class MainVM(
104140
packageRepository.getPackagesFlow(),
105141
blocklistRepository.getBlocklist(),
106142
homeSortFilterModelFlow,
143+
extras,
107144
searchQuery,
108145
selection,
109-
) { packages, blocklist, sortFilter, search, selection ->
146+
) { args ->
147+
val packages: List<Package> = args[0] as List<Package>
148+
val blocklist: Set<String> = args[1] as Set<String>
149+
val sortFilter: SortFilterModel = args[2] as SortFilterModel
150+
val extras: Map<String, AppExtras> = args[3] as Map<String, AppExtras>
151+
val search: String = args[4] as String
152+
val selection: Set<String> = args[5] as Set<String>
153+
110154
val (filteredPackages, updatedPackages) = packages
111155
.filterNot { it.packageName in blocklist }
112156
.let {
113157
Pair(
114-
it.applySearchAndFilter(search, emptyMap(), sortFilter),
158+
it.applySearch(search, extras)
159+
.applyFilter(sortFilter, extras.mapValues { it.value.customTags }),
115160
it.filter { item ->
116161
item.isUpdated || (pref_newAndUpdatedNotification.value && item.isNew)
117162
}
@@ -125,7 +170,7 @@ class MainVM(
125170
blocklist = blocklist,
126171
searchQuery = search,
127172
sortFilter = sortFilter,
128-
selection = selection
173+
selection = selection,
129174
)
130175
}.collect { newState ->
131176
homeState.update { newState }
@@ -136,20 +181,29 @@ class MainVM(
136181
packageRepository.getPackagesFlow(),
137182
blocklistRepository.getBlocklist(),
138183
backupSortFilterModelFlow,
184+
extras,
139185
searchQuery,
140186
selection,
141-
) { packages, blocklist, sortFilter, search, selection ->
187+
) { args ->
188+
val packages: List<Package> = args[0] as List<Package>
189+
val blocklist: Set<String> = args[1] as Set<String>
190+
val sortFilter: SortFilterModel = args[2] as SortFilterModel
191+
val extras: Map<String, AppExtras> = args[3] as Map<String, AppExtras>
192+
val search: String = args[4] as String
193+
val selection: Set<String> = args[5] as Set<String>
194+
142195
val filteredPackages = packages
143196
.filterNot { it.packageName in blocklist }
144-
.applySearchAndFilter(search, emptyMap(), sortFilter)
197+
.applySearch(search, extras)
198+
.applyFilter(sortFilter, extras.mapValues { it.value.customTags })
145199

146200
MainState(
147201
packages = packages,
148202
filteredPackages = filteredPackages,
149203
blocklist = blocklist,
150204
searchQuery = search,
151205
sortFilter = sortFilter,
152-
selection = selection
206+
selection = selection,
153207
)
154208
}.collect { newState ->
155209
backupState.update { newState }
@@ -160,20 +214,29 @@ class MainVM(
160214
packageRepository.getPackagesFlow(),
161215
blocklistRepository.getBlocklist(),
162216
restoreSortFilterModelFlow,
217+
extras,
163218
searchQuery,
164219
selection,
165-
) { packages, blocklist, sortFilter, search, selection ->
220+
) { args ->
221+
val packages: List<Package> = args[0] as List<Package>
222+
val blocklist: Set<String> = args[1] as Set<String>
223+
val sortFilter: SortFilterModel = args[2] as SortFilterModel
224+
val extras: Map<String, AppExtras> = args[3] as Map<String, AppExtras>
225+
val search: String = args[4] as String
226+
val selection: Set<String> = args[5] as Set<String>
227+
166228
val filteredPackages = packages
167229
.filterNot { it.packageName in blocklist }
168-
.applySearchAndFilter(search, emptyMap(), sortFilter)
230+
.applySearch(search, extras)
231+
.applyFilter(sortFilter, extras.mapValues { it.value.customTags })
169232

170233
MainState(
171234
packages = packages,
172235
filteredPackages = filteredPackages,
173236
blocklist = blocklist,
174237
searchQuery = search,
175238
sortFilter = sortFilter,
176-
selection = selection
239+
selection = selection,
177240
)
178241
}.collect { newState ->
179242
restoreState.update { newState }
@@ -198,6 +261,7 @@ class MainVM(
198261
prefs.updatedFilterBackup.set(value.updatedFilter)
199262
prefs.latestFilterBackup.set(value.latestFilter)
200263
prefs.enabledFilterBackup.set(value.enabledFilter)
264+
prefs.tagsFilterBackup.set(value.tags)
201265
}
202266

203267
NavItem.Restore -> {
@@ -210,6 +274,7 @@ class MainVM(
210274
prefs.updatedFilterRestore.set(value.updatedFilter)
211275
prefs.latestFilterRestore.set(value.latestFilter)
212276
prefs.enabledFilterRestore.set(value.enabledFilter)
277+
prefs.tagsFilterRestore.set(value.tags)
213278
}
214279

215280
else -> {
@@ -222,6 +287,7 @@ class MainVM(
222287
prefs.updatedFilterHome.set(value.updatedFilter)
223288
prefs.latestFilterHome.set(value.latestFilter)
224289
prefs.enabledFilterHome.set(value.enabledFilter)
290+
prefs.tagsFilterHome.set(value.tags)
225291
}
226292
}
227293
}

0 commit comments

Comments
 (0)