Refactor search

This commit is contained in:
MM20
2024-07-17 23:27:00 +02:00
parent 5a28eb584e
commit c7804296d7
6 changed files with 351 additions and 207 deletions
+1
View File
@@ -53,6 +53,7 @@ dependencies {
implementation(project(":core:base"))
implementation(project(":core:preferences"))
implementation(project(":core:profiles"))
implementation(project(":core:crashreporter"))
implementation(project(":core:ktx"))
}
@@ -18,6 +18,7 @@ val searchModule = module {
get(named<Website>()),
get(),
get(),
get(),
)
}
}
@@ -3,19 +3,24 @@ package de.mm20.launcher2.search
import de.mm20.launcher2.calculator.CalculatorRepository
import de.mm20.launcher2.data.customattrs.CustomAttributesRepository
import de.mm20.launcher2.data.customattrs.utils.withCustomLabels
import de.mm20.launcher2.profiles.Profile
import de.mm20.launcher2.profiles.ProfileManager
import de.mm20.launcher2.search.data.Calculator
import de.mm20.launcher2.search.data.UnitConverter
import de.mm20.launcher2.searchactions.SearchActionService
import de.mm20.launcher2.searchactions.actions.SearchAction
import de.mm20.launcher2.unitconverter.UnitConverterRepository
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.channelFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.emitAll
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.shareIn
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import kotlinx.coroutines.supervisorScope
@@ -25,6 +30,8 @@ interface SearchService {
query: String,
filters: SearchFilters,
): Flow<SearchResults>
fun getAllApps(): Flow<AllAppsResults>
}
internal class SearchServiceImpl(
@@ -40,14 +47,56 @@ internal class SearchServiceImpl(
private val websiteRepository: SearchableRepository<Website>,
private val searchActionService: SearchActionService,
private val customAttributesRepository: CustomAttributesRepository,
private val profileManager: ProfileManager,
) : SearchService {
override fun search(
query: String,
filters: SearchFilters,
): Flow<SearchResults> = channelFlow {
val results = MutableStateFlow(SearchResults())
): Flow<SearchResults> = flow {
supervisorScope {
val results = MutableStateFlow(SearchResults())
val customAttrResults = customAttributesRepository.search(query)
.map { items ->
val apps = mutableListOf<Application>()
val shortcuts = mutableListOf<AppShortcut>()
val contacts = mutableListOf<Contact>()
val events = mutableListOf<CalendarEvent>()
val files = mutableListOf<File>()
val unitConverters = mutableListOf<UnitConverter>()
val websites = mutableListOf<Website>()
val wikipedia = mutableListOf<Article>()
val locations = mutableListOf<Location>()
val searchActions = mutableListOf<SearchAction>()
for (it in items) {
when (it) {
is Application -> if (filters.apps) apps.add(it)
is AppShortcut -> if (filters.shortcuts) shortcuts.add(it)
is Contact -> if (filters.contacts) contacts.add(it)
is CalendarEvent -> if (filters.events) events.add(it)
is File -> if (filters.files) files.add(it)
is UnitConverter -> if (filters.tools) unitConverters.add(it)
is Website -> if (filters.websites) websites.add(it)
is Article -> if (filters.articles) wikipedia.add(it)
is Location -> if (filters.places) locations.add(it)
is SearchAction -> searchActions.add(it)
}
}
SearchResults(
apps = apps,
shortcuts = shortcuts,
contacts = contacts,
calendars = events,
files = files,
unitConverters = unitConverters,
websites = websites,
wikipedia = wikipedia,
locations = locations,
searchActions = searchActions,
)
}.shareIn(this, SharingStarted.WhileSubscribed(), 1)
launch {
searchActionService.search(query)
.collectLatest { r ->
@@ -59,10 +108,14 @@ internal class SearchServiceImpl(
if (filters.apps) {
launch {
appRepository.search(query, filters.allowNetwork)
.combine(customAttrResults) { apps, customAttrs ->
if (customAttrs.apps != null) apps + customAttrs.apps
else apps
}
.withCustomLabels(customAttributesRepository)
.collectLatest { r ->
results.update {
it.copy(apps = r.toImmutableList())
it.copy(apps = r)
}
}
}
@@ -70,10 +123,14 @@ internal class SearchServiceImpl(
if (filters.shortcuts) {
launch {
appShortcutRepository.search(query, filters.allowNetwork)
.combine(customAttrResults) { shortcuts, customAttrs ->
if (customAttrs.shortcuts != null) shortcuts + customAttrs.shortcuts
else shortcuts
}
.withCustomLabels(customAttributesRepository)
.collectLatest { r ->
results.update {
it.copy(shortcuts = r.toImmutableList())
it.copy(shortcuts = r)
}
}
}
@@ -81,10 +138,14 @@ internal class SearchServiceImpl(
if (filters.contacts) {
launch {
contactRepository.search(query, filters.allowNetwork)
.combine(customAttrResults) { contacts, customAttrs ->
if (customAttrs.contacts != null) contacts + customAttrs.contacts
else contacts
}
.withCustomLabels(customAttributesRepository)
.collectLatest { r ->
results.update {
it.copy(contacts = r.toImmutableList())
it.copy(contacts = r)
}
}
}
@@ -92,10 +153,14 @@ internal class SearchServiceImpl(
if (filters.events) {
launch {
calendarRepository.search(query, filters.allowNetwork)
.combine(customAttrResults) { calendars, customAttrs ->
if (customAttrs.calendars != null) calendars + customAttrs.calendars
else calendars
}
.withCustomLabels(customAttributesRepository)
.collectLatest { r ->
results.update {
it.copy(calendars = r.toImmutableList())
it.copy(calendars = r)
}
}
}
@@ -104,8 +169,8 @@ internal class SearchServiceImpl(
launch {
calculatorRepository.search(query).collectLatest { r ->
results.update {
it.copy(calculators = r?.let { persistentListOf(it) }
?: persistentListOf())
it.copy(calculators = r?.let { listOf(it) }
?: listOf())
}
}
}
@@ -113,8 +178,8 @@ internal class SearchServiceImpl(
unitConverterRepository.search(query)
.collectLatest { r ->
results.update {
it.copy(unitConverters = r?.let { persistentListOf(it) }
?: persistentListOf())
it.copy(unitConverters = r?.let { listOf(it) }
?: listOf())
}
}
}
@@ -122,10 +187,14 @@ internal class SearchServiceImpl(
if (filters.websites) {
launch {
websiteRepository.search(query, filters.allowNetwork)
.combine(customAttrResults) { websites, customAttrs ->
if (customAttrs.websites != null) websites + customAttrs.websites
else websites
}
.withCustomLabels(customAttributesRepository)
.collectLatest { r ->
results.update {
it.copy(websites = r.toImmutableList())
it.copy(websites = r)
}
}
}
@@ -134,10 +203,14 @@ internal class SearchServiceImpl(
launch {
delay(750)
articleRepository.search(query, filters.allowNetwork)
.combine(customAttrResults) { articles, customAttrs ->
if (customAttrs.wikipedia != null) articles + customAttrs.wikipedia
else articles
}
.withCustomLabels(customAttributesRepository)
.collectLatest { r ->
results.update {
it.copy(wikipedia = r.toImmutableList())
it.copy(wikipedia = r)
}
}
}
@@ -145,10 +218,14 @@ internal class SearchServiceImpl(
if (filters.places) {
launch {
locationRepository.search(query, filters.allowNetwork)
.combine(customAttrResults) { locations, customAttrs ->
if (customAttrs.locations != null) locations + customAttrs.locations
else locations
}
.withCustomLabels(customAttributesRepository)
.collectLatest { r ->
results.update {
it.copy(locations = r.toImmutableList())
it.copy(locations = r)
}
}
}
@@ -159,56 +236,65 @@ internal class SearchServiceImpl(
query,
filters.allowNetwork
)
.combine(customAttrResults) { files, customAttrs ->
if (customAttrs.files != null) files + customAttrs.files
else files
}
.withCustomLabels(customAttributesRepository)
.collectLatest { r ->
results.update {
it.copy(files = r.toImmutableList())
it.copy(files = r)
}
}
}
}
launch {
customAttributesRepository.search(query)
.withCustomLabels(customAttributesRepository)
.collectLatest { r ->
results.update {
it.copy(other = r
.filter {
filters.apps && it is Application ||
filters.shortcuts && it is AppShortcut ||
filters.contacts && it is Contact ||
filters.events && it is CalendarEvent ||
filters.files && it is File ||
filters.websites && it is Website ||
filters.articles && it is Article ||
filters.places && it is Location
}
.toImmutableList()
)
}
}
}
launch {
results.collectLatest { send(it) }
}
emitAll(results)
}
}
override fun getAllApps(): Flow<AllAppsResults> {
return profileManager.activeProfiles.flatMapLatest { profiles ->
val standardProfile = profiles.find { it.type == Profile.Type.Personal }
val workProfile = profiles.find { it.type == Profile.Type.Work }
val privateSpace = profiles.find { it.type == Profile.Type.Private }
appRepository.search("", false)
.withCustomLabels(customAttributesRepository)
.map {
val grouped = it.groupBy { it.user }
val standardProfileApps =
standardProfile?.let { grouped[it.userHandle] } ?: emptyList()
val workProfileApps = workProfile?.let { grouped[it.userHandle] } ?: emptyList()
val privateSpaceApps =
privateSpace?.let { grouped[it.userHandle] } ?: emptyList()
AllAppsResults(
standardProfileApps = standardProfileApps.sorted(),
workProfileApps = workProfileApps.sorted(),
privateSpaceApps = privateSpaceApps.sorted(),
)
}
}
}
}
data class SearchResults(
val apps: ImmutableList<Application>? = null,
val shortcuts: ImmutableList<AppShortcut>? = null,
val contacts: ImmutableList<Contact>? = null,
val calendars: ImmutableList<CalendarEvent>? = null,
val files: ImmutableList<File>? = null,
val calculators: ImmutableList<Calculator>? = null,
val unitConverters: ImmutableList<UnitConverter>? = null,
val websites: ImmutableList<Website>? = null,
val wikipedia: ImmutableList<Article>? = null,
val locations: ImmutableList<Location>? = null,
val searchActions: ImmutableList<SearchAction>? = null,
val other: ImmutableList<SavableSearchable>? = null,
val apps: List<Application>? = null,
val shortcuts: List<AppShortcut>? = null,
val contacts: List<Contact>? = null,
val calendars: List<CalendarEvent>? = null,
val files: List<File>? = null,
val calculators: List<Calculator>? = null,
val unitConverters: List<UnitConverter>? = null,
val websites: List<Website>? = null,
val wikipedia: List<Article>? = null,
val locations: List<Location>? = null,
val searchActions: List<SearchAction>? = null,
)
data class AllAppsResults(
val standardProfileApps: List<Application>,
val workProfileApps: List<Application>,
val privateSpaceApps: List<Application>,
)
fun SearchResults.toList(): List<Searchable> {
@@ -223,6 +309,5 @@ fun SearchResults.toList(): List<Searchable> {
websites,
wikipedia,
searchActions,
other,
).flatten()
}