Optimizations

This commit is contained in:
MM20 2023-02-19 23:39:14 +01:00
parent 722652e982
commit e5fe3a8816
No known key found for this signature in database
GPG Key ID: 0B61A8F2DEAFA389
7 changed files with 160 additions and 121 deletions

View File

@ -1,14 +1,11 @@
package de.mm20.launcher2.ui.launcher.search
import android.content.Context
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.asLiveData
import androidx.lifecycle.map
import androidx.lifecycle.viewModelScope
import de.mm20.launcher2.favorites.FavoritesRepository
import de.mm20.launcher2.permissions.PermissionGroup
@ -28,20 +25,18 @@ import de.mm20.launcher2.search.data.Website
import de.mm20.launcher2.search.data.Wikipedia
import de.mm20.launcher2.searchactions.actions.SearchAction
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.ensureActive
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.shareIn
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
@ -111,7 +106,6 @@ class SearchVM : ViewModel(), KoinComponent {
}
hideFavorites.postValue(query.isNotEmpty())
searchJob = viewModelScope.launch {
dataStore.data.collectLatest {
searchService.search(
query,
@ -124,6 +118,22 @@ class SearchVM : ViewModel(), KoinComponent {
websites = it.websiteSearch,
wikipedia = it.wikipediaSearch,
).collectLatest { results ->
val resultsList = withContext(Dispatchers.Default) {
listOfNotNull(
results.apps,
results.other,
results.shortcuts,
results.files,
results.contacts,
results.calendars,
results.wikipedia,
results.websites,
results.calculators,
results.unitConverters,
results.searchActions,
).flatten().sortedBy { (it as? SavableSearchable)?.label }
}
hiddenItemKeys.collectLatest { hiddenKeys ->
val hidden = mutableListOf<SavableSearchable>()
val apps = mutableListOf<LauncherApp>()
@ -137,12 +147,11 @@ class SearchVM : ViewModel(), KoinComponent {
val wikipedia = mutableListOf<Wikipedia>()
val website = mutableListOf<Website>()
val actions = mutableListOf<SearchAction>()
for (r in results) {
for (r in resultsList) {
when {
r is SavableSearchable && hiddenKeys.contains(r.key) -> {
hidden.add(r)
}
r is LauncherApp && !r.isMainProfile -> workApps.add(r)
r is LauncherApp -> apps.add(r)
r is AppShortcut -> shortcuts.add(r)
@ -156,6 +165,7 @@ class SearchVM : ViewModel(), KoinComponent {
r is SearchAction -> actions.add(r)
}
}
if (query.isNotEmpty() && launchOnEnter.value) {
bestMatch.value = listOf(
apps,
@ -170,7 +180,6 @@ class SearchVM : ViewModel(), KoinComponent {
).firstNotNullOfOrNull { it.firstOrNull() }
}
searchActionResults.value = actions
appResults.value = apps
workAppResults.value = workApps
appShortcutResults.value = shortcuts
@ -182,6 +191,7 @@ class SearchVM : ViewModel(), KoinComponent {
calculatorResults.value = calc
unitConverterResults.value = unitConv
hiddenResults.value = hidden
if (results.searchActions != null) searchActionResults.value = actions
}
}
}

View File

@ -33,9 +33,7 @@ class HiddenItemsSettingsScreenVM : ViewModel(), KoinComponent {
withContext(Dispatchers.Default) { it.sorted() }
}.asLiveData()
val hiddenItems: LiveData<List<SavableSearchable>> = liveData {
val hidden = withContext(Dispatchers.Default) {
favoritesRepository.getHiddenItems().first().filter { it !is LauncherApp }.sorted()
}
val hidden = favoritesRepository.getHiddenItems().first().filter { it !is LauncherApp }.sorted()
emit(hidden)
}

View File

@ -21,7 +21,6 @@ import de.mm20.launcher2.searchactions.builders.WebsearchActionBuilder
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
import java.io.File
@ -51,15 +50,13 @@ class EditSearchActionSheetVM : ViewModel(), KoinComponent {
}
fun getSearchableApps(context: Context) = flow {
val items = withContext(Dispatchers.Default) {
searchActionService.getSearchActivities().map {
SearchableApp(
label = context.packageManager.getActivityInfo(it, 0)
.loadLabel(context.packageManager).toString(),
componentName = it
)
}.sortedBy { it.label.romanize().lowercase() }
}
val items = searchActionService.getSearchActivities().map {
SearchableApp(
label = context.packageManager.getActivityInfo(it, 0)
.loadLabel(context.packageManager).toString(),
componentName = it
)
}.sortedBy { it.label.romanize().lowercase() }
emit(items)
}
@ -122,6 +119,7 @@ class EditSearchActionSheetVM : ViewModel(), KoinComponent {
is AppSearchActionBuilder -> action.also {
it.baseIntent.setComponent(componentName)
}
is WebsearchActionBuilder -> action
}
@ -130,6 +128,7 @@ class EditSearchActionSheetVM : ViewModel(), KoinComponent {
val initWebsearchUrl = mutableStateOf("")
/**
* Last imported URL that failed (if the current URL is equal to this, show an error banner)
*/
@ -138,7 +137,8 @@ class EditSearchActionSheetVM : ViewModel(), KoinComponent {
derivedStateOf { websearchImportErrorUrl.value == initWebsearchUrl.value }
val loadingWebsearch = mutableStateOf(false)
val skipWebsearchImport = derivedStateOf { websearchImportError.value || initWebsearchUrl.value.isEmpty() }
val skipWebsearchImport =
derivedStateOf { websearchImportError.value || initWebsearchUrl.value.isEmpty() }
fun importWebsearch(density: Density) {
if (loadingWebsearch.value) return
@ -180,14 +180,15 @@ class EditSearchActionSheetVM : ViewModel(), KoinComponent {
private val invalidWebsearchUrl = mutableStateOf<String?>(null)
val websearchInvalidUrlError = derivedStateOf { invalidWebsearchUrl.value == (searchAction.value as? WebsearchActionBuilder)?.urlTemplate }
val websearchInvalidUrlError =
derivedStateOf { invalidWebsearchUrl.value == (searchAction.value as? WebsearchActionBuilder)?.urlTemplate }
val customIntentKeyError = mutableStateOf(false)
fun validate() : Boolean {
fun validate(): Boolean {
val action = searchAction.value ?: return false
if (action is WebsearchActionBuilder) {
val valid = action.urlTemplate.contains("\${1}")
invalidWebsearchUrl.value = if(valid) null else action.urlTemplate
invalidWebsearchUrl.value = if (valid) null else action.urlTemplate
return valid
}
if (action is CustomIntentActionBuilder) {
@ -216,9 +217,14 @@ class EditSearchActionSheetVM : ViewModel(), KoinComponent {
if (action.customIcon != initialCustomIcon) {
deleteCustomIcon(action.customIcon)
}
searchAction.value = when(action) {
searchAction.value = when (action) {
is WebsearchActionBuilder -> action.copy(icon = icon, customIcon = null, iconColor = 0)
is CustomIntentActionBuilder -> action.copy(icon = icon, customIcon = null, iconColor = 0)
is CustomIntentActionBuilder -> action.copy(
icon = icon,
customIcon = null,
iconColor = 0
)
is AppSearchActionBuilder -> action.copy(icon = icon, customIcon = null, iconColor = 0)
}
}
@ -228,10 +234,24 @@ class EditSearchActionSheetVM : ViewModel(), KoinComponent {
if (action.customIcon != initialCustomIcon) {
deleteCustomIcon(action.customIcon)
}
searchAction.value = when(action) {
is WebsearchActionBuilder -> action.copy(customIcon = iconPath, iconColor = 1, icon = SearchActionIcon.Custom)
is CustomIntentActionBuilder -> action.copy(customIcon = iconPath, iconColor = 1, icon = SearchActionIcon.Custom)
is AppSearchActionBuilder -> action.copy(customIcon = iconPath, iconColor = 1, icon = SearchActionIcon.Custom)
searchAction.value = when (action) {
is WebsearchActionBuilder -> action.copy(
customIcon = iconPath,
iconColor = 1,
icon = SearchActionIcon.Custom
)
is CustomIntentActionBuilder -> action.copy(
customIcon = iconPath,
iconColor = 1,
icon = SearchActionIcon.Custom
)
is AppSearchActionBuilder -> action.copy(
customIcon = iconPath,
iconColor = 1,
icon = SearchActionIcon.Custom
)
}
}
@ -247,7 +267,7 @@ class EditSearchActionSheetVM : ViewModel(), KoinComponent {
}
fun applyIcon() {
currentPage.value = when(searchAction.value) {
currentPage.value = when (searchAction.value) {
is AppSearchActionBuilder -> EditSearchActionPage.CustomizeAppSearch
is WebsearchActionBuilder -> EditSearchActionPage.CustomizeWebSearch
is CustomIntentActionBuilder -> EditSearchActionPage.CustomizeCustomIntent
@ -257,7 +277,7 @@ class EditSearchActionSheetVM : ViewModel(), KoinComponent {
fun setIconColor(color: Int) {
val action = searchAction.value ?: return
searchAction.value = when(action) {
searchAction.value = when (action) {
is WebsearchActionBuilder -> action.copy(iconColor = color)
is CustomIntentActionBuilder -> action.copy(iconColor = color)
is AppSearchActionBuilder -> action.copy(iconColor = color)
@ -273,7 +293,7 @@ class EditSearchActionSheetVM : ViewModel(), KoinComponent {
fun removeExtra(key: String) {
val action = searchAction.value ?: return
searchAction.value = when(action) {
searchAction.value = when (action) {
is CustomIntentActionBuilder -> action.copy(
baseIntent = action.baseIntent.cloneFilter().also {
val extras = action.baseIntent.extras?.deepCopy() ?: Bundle()
@ -281,6 +301,7 @@ class EditSearchActionSheetVM : ViewModel(), KoinComponent {
it.replaceExtras(extras)
},
)
is AppSearchActionBuilder -> action.copy(
baseIntent = action.baseIntent.cloneFilter().also {
val extras = action.baseIntent.extras?.deepCopy() ?: Bundle()
@ -288,13 +309,14 @@ class EditSearchActionSheetVM : ViewModel(), KoinComponent {
it.replaceExtras(extras)
}
)
else -> action
}
}
fun putStringExtra(key: String, value: String = "") {
val action = searchAction.value ?: return
searchAction.value = when(action) {
searchAction.value = when (action) {
is CustomIntentActionBuilder -> action.copy(
baseIntent = action.baseIntent.cloneFilter().also {
val extras = action.baseIntent.extras?.deepCopy() ?: Bundle()
@ -302,6 +324,7 @@ class EditSearchActionSheetVM : ViewModel(), KoinComponent {
it.replaceExtras(extras)
},
)
is AppSearchActionBuilder -> action.copy(
baseIntent = action.baseIntent.cloneFilter().also {
val extras = action.baseIntent.extras?.deepCopy() ?: Bundle()
@ -309,13 +332,14 @@ class EditSearchActionSheetVM : ViewModel(), KoinComponent {
it.replaceExtras(extras)
},
)
else -> action
}
}
fun putIntExtra(key: String, value: Int = 0) {
val action = searchAction.value ?: return
searchAction.value = when(action) {
searchAction.value = when (action) {
is CustomIntentActionBuilder -> action.copy(
baseIntent = action.baseIntent.cloneFilter().also {
val extras = action.baseIntent.extras?.deepCopy() ?: Bundle()
@ -323,6 +347,7 @@ class EditSearchActionSheetVM : ViewModel(), KoinComponent {
it.replaceExtras(extras)
},
)
is AppSearchActionBuilder -> action.copy(
baseIntent = action.baseIntent.cloneFilter().also {
val extras = action.baseIntent.extras?.deepCopy() ?: Bundle()
@ -330,13 +355,14 @@ class EditSearchActionSheetVM : ViewModel(), KoinComponent {
it.replaceExtras(extras)
},
)
else -> action
}
}
fun putLongExtra(key: String, value: Long = 0L) {
val action = searchAction.value ?: return
searchAction.value = when(action) {
searchAction.value = when (action) {
is CustomIntentActionBuilder -> action.copy(
baseIntent = action.baseIntent.cloneFilter().also {
val extras = action.baseIntent.extras?.deepCopy() ?: Bundle()
@ -344,6 +370,7 @@ class EditSearchActionSheetVM : ViewModel(), KoinComponent {
it.replaceExtras(extras)
},
)
is AppSearchActionBuilder -> action.copy(
baseIntent = action.baseIntent.cloneFilter().also {
val extras = action.baseIntent.extras?.deepCopy() ?: Bundle()
@ -351,13 +378,14 @@ class EditSearchActionSheetVM : ViewModel(), KoinComponent {
it.replaceExtras(extras)
},
)
else -> action
}
}
fun putFloatExtra(key: String, value: Float = 0f) {
val action = searchAction.value ?: return
searchAction.value = when(action) {
searchAction.value = when (action) {
is CustomIntentActionBuilder -> action.copy(
baseIntent = action.baseIntent.cloneFilter().also {
val extras = action.baseIntent.extras?.deepCopy() ?: Bundle()
@ -365,6 +393,7 @@ class EditSearchActionSheetVM : ViewModel(), KoinComponent {
it.replaceExtras(extras)
},
)
is AppSearchActionBuilder -> action.copy(
baseIntent = action.baseIntent.cloneFilter().also {
val extras = action.baseIntent.extras?.deepCopy() ?: Bundle()
@ -372,13 +401,14 @@ class EditSearchActionSheetVM : ViewModel(), KoinComponent {
it.replaceExtras(extras)
},
)
else -> action
}
}
fun putDoubleExtra(key: String, value: Double = 0.0) {
val action = searchAction.value ?: return
searchAction.value = when(action) {
searchAction.value = when (action) {
is CustomIntentActionBuilder -> action.copy(
baseIntent = action.baseIntent.cloneFilter().also {
val extras = action.baseIntent.extras?.deepCopy() ?: Bundle()
@ -386,6 +416,7 @@ class EditSearchActionSheetVM : ViewModel(), KoinComponent {
it.replaceExtras(extras)
},
)
is AppSearchActionBuilder -> action.copy(
baseIntent = action.baseIntent.cloneFilter().also {
val extras = action.baseIntent.extras?.deepCopy() ?: Bundle()
@ -393,13 +424,14 @@ class EditSearchActionSheetVM : ViewModel(), KoinComponent {
it.replaceExtras(extras)
},
)
else -> action
}
}
fun putBooleanExtra(key: String, value: Boolean = false) {
val action = searchAction.value ?: return
searchAction.value = when(action) {
searchAction.value = when (action) {
is CustomIntentActionBuilder -> action.copy(
baseIntent = action.baseIntent.cloneFilter().also {
val extras = action.baseIntent.extras?.deepCopy() ?: Bundle()
@ -407,6 +439,7 @@ class EditSearchActionSheetVM : ViewModel(), KoinComponent {
it.replaceExtras(extras)
},
)
is AppSearchActionBuilder -> action.copy(
baseIntent = action.baseIntent.cloneFilter().also {
val extras = action.baseIntent.extras?.deepCopy() ?: Bundle()
@ -414,13 +447,14 @@ class EditSearchActionSheetVM : ViewModel(), KoinComponent {
it.replaceExtras(extras)
},
)
else -> action
}
}
fun setIntentAction(action: String) {
val searchAction = searchAction.value ?: return
this.searchAction.value = when(searchAction) {
this.searchAction.value = when (searchAction) {
is CustomIntentActionBuilder -> searchAction.copy(
baseIntent = searchAction.baseIntent.cloneFilter().also {
val extras = searchAction.baseIntent.extras?.deepCopy() ?: Bundle()
@ -428,13 +462,14 @@ class EditSearchActionSheetVM : ViewModel(), KoinComponent {
it.action = action
},
)
else -> searchAction
}
}
fun setIntentCategory(category: String) {
val action = searchAction.value ?: return
searchAction.value = when(action) {
searchAction.value = when (action) {
is CustomIntentActionBuilder -> action.copy(
baseIntent = action.baseIntent.cloneFilter().also {
val extras = action.baseIntent.extras?.deepCopy() ?: Bundle()
@ -444,26 +479,29 @@ class EditSearchActionSheetVM : ViewModel(), KoinComponent {
if (category.isNotBlank()) it.addCategory(category)
},
)
else -> action
}
}
fun setIntentQueryExtra(key: String) {
val action = searchAction.value ?: return
searchAction.value = when(action) {
searchAction.value = when (action) {
is CustomIntentActionBuilder -> action.copy(
queryKey = key,
)
else -> action
}
}
fun setQueryEncoding(encoding: WebsearchActionBuilder.QueryEncoding) {
val action = searchAction.value ?: return
searchAction.value = when(action) {
searchAction.value = when (action) {
is WebsearchActionBuilder -> action.copy(
encoding = encoding
)
else -> action
}
}

View File

@ -16,10 +16,12 @@ import de.mm20.launcher2.search.data.LauncherApp
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.withContext
import org.apache.commons.text.similarity.FuzzyScore
import java.util.*
import java.util.Locale
interface AppRepository {
fun getAllInstalledApps(): Flow<List<LauncherApp>>
@ -141,9 +143,8 @@ internal class AppRepositoryImpl(
return LauncherApp(context, launcherActivityInfo)
}
override fun search(query: String): Flow<ImmutableList<LauncherApp>> = channelFlow {
installedApps.collectLatest { apps ->
override fun search(query: String): Flow<ImmutableList<LauncherApp>> {
return installedApps.map { apps ->
withContext(Dispatchers.Default) {
val appResults = mutableListOf<LauncherApp>()
if (query.isEmpty()) {
@ -156,10 +157,8 @@ internal class AppRepositoryImpl(
val componentName = ComponentName.unflattenFromString(query)
getActivityByComponentName(componentName)?.let { appResults.add(it) }
}
appResults.sort()
send(appResults.toImmutableList())
appResults.toImmutableList()
}
}
}

View File

@ -21,8 +21,7 @@ import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.emitAll
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.withContext
import okhttp3.OkHttpClient
@ -37,7 +36,7 @@ import java.net.URL
import java.util.UUID
interface SearchActionService {
fun search(query: String): Flow<ImmutableList<SearchAction>>
suspend fun search(query: String): Flow<ImmutableList<SearchAction>>
fun getSearchActionBuilders(): Flow<List<SearchActionBuilder>>
fun getDisabledActionBuilders(): Flow<List<SearchActionBuilder>>
@ -56,23 +55,21 @@ internal class SearchActionServiceImpl(
private val repository: SearchActionRepository,
private val textClassifier: TextClassifier,
) : SearchActionService {
override fun search(
override suspend fun search(
query: String
): Flow<ImmutableList<SearchAction>> = flow {
): Flow<ImmutableList<SearchAction>> {
if (query.isBlank()) {
emit(persistentListOf())
return@flow
return flowOf(persistentListOf())
}
val classificationResult = textClassifier.classify(context, query)
val builders = repository.getSearchActionBuilders()
emitAll(
builders.map {
it.mapNotNull { it.build(context, classificationResult) }.toImmutableList()
}
)
return builders.map {
it.mapNotNull { it.build(context, classificationResult) }.toImmutableList()
}
}
override fun getSearchActionBuilders(): Flow<List<SearchActionBuilder>> {
@ -136,7 +133,10 @@ internal class SearchActionServiceImpl(
return@withContext null
}
private suspend fun importOpenSearch(openSearchHref: String, iconSize: Int): WebsearchActionBuilder? {
private suspend fun importOpenSearch(
openSearchHref: String,
iconSize: Int
): WebsearchActionBuilder? {
try {
val httpClient = OkHttpClient()
val request = Request.Builder()
@ -217,7 +217,8 @@ internal class SearchActionServiceImpl(
}
} catch (e: IOException) {
} catch (e: XmlPullParserException) {}
} catch (e: XmlPullParserException) {
}
return null
}
@ -242,7 +243,8 @@ internal class SearchActionServiceImpl(
override suspend fun getSearchActivities(): List<ComponentName> {
return withContext(Dispatchers.Default) {
val resolveInfos = context.packageManager.queryIntentActivities(
Intent(Intent.ACTION_SEARCH).addCategory(Intent.CATEGORY_DEFAULT), PackageManager.GET_META_DATA,
Intent(Intent.ACTION_SEARCH).addCategory(Intent.CATEGORY_DEFAULT),
PackageManager.GET_META_DATA,
)
resolveInfos.mapNotNull {
if (!it.activityInfo.exported || !it.activityInfo.enabled) return@mapNotNull null

View File

@ -51,7 +51,7 @@ class IconPackManager(
}
suspend fun updateIconPacks() {
withContext(Dispatchers.Default) {
withContext(Dispatchers.IO) {
IconPackInstaller(context, appDatabase).installIcons()
GrayscaleMapInstaller(context, appDatabase).installIcons()
}

View File

@ -30,8 +30,8 @@ import de.mm20.launcher2.search.data.OwncloudFile
import de.mm20.launcher2.search.data.UnitConverter
import de.mm20.launcher2.search.data.Website
import de.mm20.launcher2.search.data.Wikipedia
import de.mm20.launcher2.searchactions.actions.SearchAction
import de.mm20.launcher2.searchactions.SearchActionService
import de.mm20.launcher2.searchactions.actions.SearchAction
import de.mm20.launcher2.unitconverter.UnitConverterRepository
import de.mm20.launcher2.websites.WebsiteRepository
import de.mm20.launcher2.wikipedia.WikipediaRepository
@ -58,7 +58,7 @@ interface SearchService {
unitConverter: UnitConverterSearchSettings,
websites: WebsiteSearchSettings,
wikipedia: WikipediaSearchSettings,
): Flow<ImmutableList<Searchable>>
): Flow<SearchResults>
}
internal class SearchServiceImpl(
@ -85,16 +85,23 @@ internal class SearchServiceImpl(
unitConverter: UnitConverterSearchSettings,
websites: WebsiteSearchSettings,
wikipedia: WikipediaSearchSettings,
): Flow<ImmutableList<Searchable>> = channelFlow {
var searchActionsReady = false
): Flow<SearchResults> = channelFlow {
val results = MutableStateFlow(SearchResults())
supervisorScope {
val results = MutableStateFlow(SearchResults())
launch {
searchActionService.search(query)
.collectLatest { r ->
results.update {
it.copy(searchActions = r)
}
}
}
launch {
appRepository.search(query)
.withCustomLabels(customAttributesRepository)
.collectLatest { r ->
results.update {
it.copy(apps = r)
it.copy(apps = r.toImmutableList())
}
}
}
@ -104,7 +111,7 @@ internal class SearchServiceImpl(
.withCustomLabels(customAttributesRepository)
.collectLatest { r ->
results.update {
it.copy(shortcuts = r)
it.copy(shortcuts = r.toImmutableList())
}
}
}
@ -115,7 +122,7 @@ internal class SearchServiceImpl(
.withCustomLabels(customAttributesRepository)
.collectLatest { r ->
results.update {
it.copy(contacts = r)
it.copy(contacts = r.toImmutableList())
}
}
}
@ -126,7 +133,7 @@ internal class SearchServiceImpl(
.withCustomLabels(customAttributesRepository)
.collectLatest { r ->
results.update {
it.copy(calendars = r)
it.copy(calendars = r.toImmutableList())
}
}
}
@ -143,12 +150,13 @@ internal class SearchServiceImpl(
}
if (unitConverter.enabled) {
launch {
unitConverterRepository.search(query, unitConverter.currencies).collectLatest { r ->
results.update {
it.copy(unitConverters = r?.let { persistentListOf(it) }
?: persistentListOf())
unitConverterRepository.search(query, unitConverter.currencies)
.collectLatest { r ->
results.update {
it.copy(unitConverters = r?.let { persistentListOf(it) }
?: persistentListOf())
}
}
}
}
}
if (websites.enabled) {
@ -158,7 +166,7 @@ internal class SearchServiceImpl(
.withCustomLabels(customAttributesRepository)
.collectLatest { r ->
results.update {
it.copy(websites = r)
it.copy(websites = r.toImmutableList())
}
}
}
@ -170,7 +178,7 @@ internal class SearchServiceImpl(
.withCustomLabels(customAttributesRepository)
.collectLatest { r ->
results.update {
it.copy(wikipedia = r)
it.copy(wikipedia = r.toImmutableList())
}
}
}
@ -188,7 +196,7 @@ internal class SearchServiceImpl(
.withCustomLabels(customAttributesRepository)
.collectLatest { r ->
results.update {
it.copy(files = r)
it.copy(files = r.toImmutableList())
}
}
}
@ -218,39 +226,23 @@ internal class SearchServiceImpl(
}
}
launch {
searchActionService.search(query)
.collectLatest { r ->
results.update {
searchActionsReady = true
it.copy(searchActions = r)
}
}
}
launch {
results
.map { it.toList().sortedBy { it as? SavableSearchable }.toImmutableList() }
.collectLatest {
if (searchActionsReady) send(it)
}
results.collectLatest { send(it) }
}
}
}
}
internal data class SearchResults(
val apps: List<LauncherApp> = emptyList(),
val shortcuts: List<AppShortcut> = emptyList(),
val contacts: List<Contact> = emptyList(),
val calendars: List<CalendarEvent> = emptyList(),
val files: List<File> = emptyList(),
val calculators: List<Calculator> = emptyList(),
val unitConverters: List<UnitConverter> = emptyList(),
val websites: List<Website> = emptyList(),
val wikipedia: List<Wikipedia> = emptyList(),
val searchActions: List<SearchAction> = emptyList(),
val other: List<SavableSearchable> = emptyList(),
) {
fun toList(): List<Searchable> {
return searchActions + (apps + shortcuts + contacts + calendars + files + websites + wikipedia + other).distinctBy { it.key } + calculators + unitConverters
}
}
data class SearchResults(
val apps: ImmutableList<LauncherApp>? = 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<Wikipedia>? = null,
val searchActions: ImmutableList<SearchAction>? = null,
val other: ImmutableList<SavableSearchable>? = null,
)