Optimizations
This commit is contained in:
parent
722652e982
commit
e5fe3a8816
@ -1,14 +1,11 @@
|
|||||||
package de.mm20.launcher2.ui.launcher.search
|
package de.mm20.launcher2.ui.launcher.search
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.util.Log
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.compose.runtime.collectAsState
|
|
||||||
import androidx.compose.runtime.livedata.observeAsState
|
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.asLiveData
|
|
||||||
import androidx.lifecycle.map
|
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import de.mm20.launcher2.favorites.FavoritesRepository
|
import de.mm20.launcher2.favorites.FavoritesRepository
|
||||||
import de.mm20.launcher2.permissions.PermissionGroup
|
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.search.data.Wikipedia
|
||||||
import de.mm20.launcher2.searchactions.actions.SearchAction
|
import de.mm20.launcher2.searchactions.actions.SearchAction
|
||||||
import kotlinx.coroutines.CancellationException
|
import kotlinx.coroutines.CancellationException
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.ensureActive
|
||||||
import kotlinx.coroutines.flow.SharingStarted
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
import kotlinx.coroutines.flow.combine
|
import kotlinx.coroutines.flow.combine
|
||||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
import kotlinx.coroutines.flow.firstOrNull
|
|
||||||
import kotlinx.coroutines.flow.launchIn
|
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.flow.mapLatest
|
|
||||||
import kotlinx.coroutines.flow.onEach
|
|
||||||
import kotlinx.coroutines.flow.shareIn
|
import kotlinx.coroutines.flow.shareIn
|
||||||
import kotlinx.coroutines.flow.stateIn
|
import kotlinx.coroutines.flow.stateIn
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
import org.koin.core.component.KoinComponent
|
import org.koin.core.component.KoinComponent
|
||||||
import org.koin.core.component.inject
|
import org.koin.core.component.inject
|
||||||
|
|
||||||
@ -111,7 +106,6 @@ class SearchVM : ViewModel(), KoinComponent {
|
|||||||
}
|
}
|
||||||
hideFavorites.postValue(query.isNotEmpty())
|
hideFavorites.postValue(query.isNotEmpty())
|
||||||
searchJob = viewModelScope.launch {
|
searchJob = viewModelScope.launch {
|
||||||
|
|
||||||
dataStore.data.collectLatest {
|
dataStore.data.collectLatest {
|
||||||
searchService.search(
|
searchService.search(
|
||||||
query,
|
query,
|
||||||
@ -124,6 +118,22 @@ class SearchVM : ViewModel(), KoinComponent {
|
|||||||
websites = it.websiteSearch,
|
websites = it.websiteSearch,
|
||||||
wikipedia = it.wikipediaSearch,
|
wikipedia = it.wikipediaSearch,
|
||||||
).collectLatest { results ->
|
).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 ->
|
hiddenItemKeys.collectLatest { hiddenKeys ->
|
||||||
val hidden = mutableListOf<SavableSearchable>()
|
val hidden = mutableListOf<SavableSearchable>()
|
||||||
val apps = mutableListOf<LauncherApp>()
|
val apps = mutableListOf<LauncherApp>()
|
||||||
@ -137,12 +147,11 @@ class SearchVM : ViewModel(), KoinComponent {
|
|||||||
val wikipedia = mutableListOf<Wikipedia>()
|
val wikipedia = mutableListOf<Wikipedia>()
|
||||||
val website = mutableListOf<Website>()
|
val website = mutableListOf<Website>()
|
||||||
val actions = mutableListOf<SearchAction>()
|
val actions = mutableListOf<SearchAction>()
|
||||||
for (r in results) {
|
for (r in resultsList) {
|
||||||
when {
|
when {
|
||||||
r is SavableSearchable && hiddenKeys.contains(r.key) -> {
|
r is SavableSearchable && hiddenKeys.contains(r.key) -> {
|
||||||
hidden.add(r)
|
hidden.add(r)
|
||||||
}
|
}
|
||||||
|
|
||||||
r is LauncherApp && !r.isMainProfile -> workApps.add(r)
|
r is LauncherApp && !r.isMainProfile -> workApps.add(r)
|
||||||
r is LauncherApp -> apps.add(r)
|
r is LauncherApp -> apps.add(r)
|
||||||
r is AppShortcut -> shortcuts.add(r)
|
r is AppShortcut -> shortcuts.add(r)
|
||||||
@ -156,6 +165,7 @@ class SearchVM : ViewModel(), KoinComponent {
|
|||||||
r is SearchAction -> actions.add(r)
|
r is SearchAction -> actions.add(r)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (query.isNotEmpty() && launchOnEnter.value) {
|
if (query.isNotEmpty() && launchOnEnter.value) {
|
||||||
bestMatch.value = listOf(
|
bestMatch.value = listOf(
|
||||||
apps,
|
apps,
|
||||||
@ -170,7 +180,6 @@ class SearchVM : ViewModel(), KoinComponent {
|
|||||||
).firstNotNullOfOrNull { it.firstOrNull() }
|
).firstNotNullOfOrNull { it.firstOrNull() }
|
||||||
}
|
}
|
||||||
|
|
||||||
searchActionResults.value = actions
|
|
||||||
appResults.value = apps
|
appResults.value = apps
|
||||||
workAppResults.value = workApps
|
workAppResults.value = workApps
|
||||||
appShortcutResults.value = shortcuts
|
appShortcutResults.value = shortcuts
|
||||||
@ -182,6 +191,7 @@ class SearchVM : ViewModel(), KoinComponent {
|
|||||||
calculatorResults.value = calc
|
calculatorResults.value = calc
|
||||||
unitConverterResults.value = unitConv
|
unitConverterResults.value = unitConv
|
||||||
hiddenResults.value = hidden
|
hiddenResults.value = hidden
|
||||||
|
if (results.searchActions != null) searchActionResults.value = actions
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -33,9 +33,7 @@ class HiddenItemsSettingsScreenVM : ViewModel(), KoinComponent {
|
|||||||
withContext(Dispatchers.Default) { it.sorted() }
|
withContext(Dispatchers.Default) { it.sorted() }
|
||||||
}.asLiveData()
|
}.asLiveData()
|
||||||
val hiddenItems: LiveData<List<SavableSearchable>> = liveData {
|
val hiddenItems: LiveData<List<SavableSearchable>> = liveData {
|
||||||
val hidden = withContext(Dispatchers.Default) {
|
val hidden = favoritesRepository.getHiddenItems().first().filter { it !is LauncherApp }.sorted()
|
||||||
favoritesRepository.getHiddenItems().first().filter { it !is LauncherApp }.sorted()
|
|
||||||
}
|
|
||||||
emit(hidden)
|
emit(hidden)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -21,7 +21,6 @@ import de.mm20.launcher2.searchactions.builders.WebsearchActionBuilder
|
|||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.flow
|
import kotlinx.coroutines.flow.flow
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
import org.koin.core.component.KoinComponent
|
import org.koin.core.component.KoinComponent
|
||||||
import org.koin.core.component.inject
|
import org.koin.core.component.inject
|
||||||
import java.io.File
|
import java.io.File
|
||||||
@ -51,15 +50,13 @@ class EditSearchActionSheetVM : ViewModel(), KoinComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun getSearchableApps(context: Context) = flow {
|
fun getSearchableApps(context: Context) = flow {
|
||||||
val items = withContext(Dispatchers.Default) {
|
val items = searchActionService.getSearchActivities().map {
|
||||||
searchActionService.getSearchActivities().map {
|
SearchableApp(
|
||||||
SearchableApp(
|
label = context.packageManager.getActivityInfo(it, 0)
|
||||||
label = context.packageManager.getActivityInfo(it, 0)
|
.loadLabel(context.packageManager).toString(),
|
||||||
.loadLabel(context.packageManager).toString(),
|
componentName = it
|
||||||
componentName = it
|
)
|
||||||
)
|
}.sortedBy { it.label.romanize().lowercase() }
|
||||||
}.sortedBy { it.label.romanize().lowercase() }
|
|
||||||
}
|
|
||||||
emit(items)
|
emit(items)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -122,6 +119,7 @@ class EditSearchActionSheetVM : ViewModel(), KoinComponent {
|
|||||||
is AppSearchActionBuilder -> action.also {
|
is AppSearchActionBuilder -> action.also {
|
||||||
it.baseIntent.setComponent(componentName)
|
it.baseIntent.setComponent(componentName)
|
||||||
}
|
}
|
||||||
|
|
||||||
is WebsearchActionBuilder -> action
|
is WebsearchActionBuilder -> action
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -130,6 +128,7 @@ class EditSearchActionSheetVM : ViewModel(), KoinComponent {
|
|||||||
|
|
||||||
|
|
||||||
val initWebsearchUrl = mutableStateOf("")
|
val initWebsearchUrl = mutableStateOf("")
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Last imported URL that failed (if the current URL is equal to this, show an error banner)
|
* 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 }
|
derivedStateOf { websearchImportErrorUrl.value == initWebsearchUrl.value }
|
||||||
val loadingWebsearch = mutableStateOf(false)
|
val loadingWebsearch = mutableStateOf(false)
|
||||||
|
|
||||||
val skipWebsearchImport = derivedStateOf { websearchImportError.value || initWebsearchUrl.value.isEmpty() }
|
val skipWebsearchImport =
|
||||||
|
derivedStateOf { websearchImportError.value || initWebsearchUrl.value.isEmpty() }
|
||||||
|
|
||||||
fun importWebsearch(density: Density) {
|
fun importWebsearch(density: Density) {
|
||||||
if (loadingWebsearch.value) return
|
if (loadingWebsearch.value) return
|
||||||
@ -180,14 +180,15 @@ class EditSearchActionSheetVM : ViewModel(), KoinComponent {
|
|||||||
|
|
||||||
|
|
||||||
private val invalidWebsearchUrl = mutableStateOf<String?>(null)
|
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)
|
val customIntentKeyError = mutableStateOf(false)
|
||||||
fun validate() : Boolean {
|
fun validate(): Boolean {
|
||||||
val action = searchAction.value ?: return false
|
val action = searchAction.value ?: return false
|
||||||
|
|
||||||
if (action is WebsearchActionBuilder) {
|
if (action is WebsearchActionBuilder) {
|
||||||
val valid = action.urlTemplate.contains("\${1}")
|
val valid = action.urlTemplate.contains("\${1}")
|
||||||
invalidWebsearchUrl.value = if(valid) null else action.urlTemplate
|
invalidWebsearchUrl.value = if (valid) null else action.urlTemplate
|
||||||
return valid
|
return valid
|
||||||
}
|
}
|
||||||
if (action is CustomIntentActionBuilder) {
|
if (action is CustomIntentActionBuilder) {
|
||||||
@ -216,9 +217,14 @@ class EditSearchActionSheetVM : ViewModel(), KoinComponent {
|
|||||||
if (action.customIcon != initialCustomIcon) {
|
if (action.customIcon != initialCustomIcon) {
|
||||||
deleteCustomIcon(action.customIcon)
|
deleteCustomIcon(action.customIcon)
|
||||||
}
|
}
|
||||||
searchAction.value = when(action) {
|
searchAction.value = when (action) {
|
||||||
is WebsearchActionBuilder -> action.copy(icon = icon, customIcon = null, iconColor = 0)
|
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)
|
is AppSearchActionBuilder -> action.copy(icon = icon, customIcon = null, iconColor = 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -228,10 +234,24 @@ class EditSearchActionSheetVM : ViewModel(), KoinComponent {
|
|||||||
if (action.customIcon != initialCustomIcon) {
|
if (action.customIcon != initialCustomIcon) {
|
||||||
deleteCustomIcon(action.customIcon)
|
deleteCustomIcon(action.customIcon)
|
||||||
}
|
}
|
||||||
searchAction.value = when(action) {
|
searchAction.value = when (action) {
|
||||||
is WebsearchActionBuilder -> action.copy(customIcon = iconPath, iconColor = 1, icon = SearchActionIcon.Custom)
|
is WebsearchActionBuilder -> action.copy(
|
||||||
is CustomIntentActionBuilder -> action.copy(customIcon = iconPath, iconColor = 1, icon = SearchActionIcon.Custom)
|
customIcon = iconPath,
|
||||||
is AppSearchActionBuilder -> action.copy(customIcon = iconPath, iconColor = 1, icon = SearchActionIcon.Custom)
|
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() {
|
fun applyIcon() {
|
||||||
currentPage.value = when(searchAction.value) {
|
currentPage.value = when (searchAction.value) {
|
||||||
is AppSearchActionBuilder -> EditSearchActionPage.CustomizeAppSearch
|
is AppSearchActionBuilder -> EditSearchActionPage.CustomizeAppSearch
|
||||||
is WebsearchActionBuilder -> EditSearchActionPage.CustomizeWebSearch
|
is WebsearchActionBuilder -> EditSearchActionPage.CustomizeWebSearch
|
||||||
is CustomIntentActionBuilder -> EditSearchActionPage.CustomizeCustomIntent
|
is CustomIntentActionBuilder -> EditSearchActionPage.CustomizeCustomIntent
|
||||||
@ -257,7 +277,7 @@ class EditSearchActionSheetVM : ViewModel(), KoinComponent {
|
|||||||
|
|
||||||
fun setIconColor(color: Int) {
|
fun setIconColor(color: Int) {
|
||||||
val action = searchAction.value ?: return
|
val action = searchAction.value ?: return
|
||||||
searchAction.value = when(action) {
|
searchAction.value = when (action) {
|
||||||
is WebsearchActionBuilder -> action.copy(iconColor = color)
|
is WebsearchActionBuilder -> action.copy(iconColor = color)
|
||||||
is CustomIntentActionBuilder -> action.copy(iconColor = color)
|
is CustomIntentActionBuilder -> action.copy(iconColor = color)
|
||||||
is AppSearchActionBuilder -> action.copy(iconColor = color)
|
is AppSearchActionBuilder -> action.copy(iconColor = color)
|
||||||
@ -273,7 +293,7 @@ class EditSearchActionSheetVM : ViewModel(), KoinComponent {
|
|||||||
|
|
||||||
fun removeExtra(key: String) {
|
fun removeExtra(key: String) {
|
||||||
val action = searchAction.value ?: return
|
val action = searchAction.value ?: return
|
||||||
searchAction.value = when(action) {
|
searchAction.value = when (action) {
|
||||||
is CustomIntentActionBuilder -> action.copy(
|
is CustomIntentActionBuilder -> action.copy(
|
||||||
baseIntent = action.baseIntent.cloneFilter().also {
|
baseIntent = action.baseIntent.cloneFilter().also {
|
||||||
val extras = action.baseIntent.extras?.deepCopy() ?: Bundle()
|
val extras = action.baseIntent.extras?.deepCopy() ?: Bundle()
|
||||||
@ -281,6 +301,7 @@ class EditSearchActionSheetVM : ViewModel(), KoinComponent {
|
|||||||
it.replaceExtras(extras)
|
it.replaceExtras(extras)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
is AppSearchActionBuilder -> action.copy(
|
is AppSearchActionBuilder -> action.copy(
|
||||||
baseIntent = action.baseIntent.cloneFilter().also {
|
baseIntent = action.baseIntent.cloneFilter().also {
|
||||||
val extras = action.baseIntent.extras?.deepCopy() ?: Bundle()
|
val extras = action.baseIntent.extras?.deepCopy() ?: Bundle()
|
||||||
@ -288,13 +309,14 @@ class EditSearchActionSheetVM : ViewModel(), KoinComponent {
|
|||||||
it.replaceExtras(extras)
|
it.replaceExtras(extras)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
else -> action
|
else -> action
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun putStringExtra(key: String, value: String = "") {
|
fun putStringExtra(key: String, value: String = "") {
|
||||||
val action = searchAction.value ?: return
|
val action = searchAction.value ?: return
|
||||||
searchAction.value = when(action) {
|
searchAction.value = when (action) {
|
||||||
is CustomIntentActionBuilder -> action.copy(
|
is CustomIntentActionBuilder -> action.copy(
|
||||||
baseIntent = action.baseIntent.cloneFilter().also {
|
baseIntent = action.baseIntent.cloneFilter().also {
|
||||||
val extras = action.baseIntent.extras?.deepCopy() ?: Bundle()
|
val extras = action.baseIntent.extras?.deepCopy() ?: Bundle()
|
||||||
@ -302,6 +324,7 @@ class EditSearchActionSheetVM : ViewModel(), KoinComponent {
|
|||||||
it.replaceExtras(extras)
|
it.replaceExtras(extras)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
is AppSearchActionBuilder -> action.copy(
|
is AppSearchActionBuilder -> action.copy(
|
||||||
baseIntent = action.baseIntent.cloneFilter().also {
|
baseIntent = action.baseIntent.cloneFilter().also {
|
||||||
val extras = action.baseIntent.extras?.deepCopy() ?: Bundle()
|
val extras = action.baseIntent.extras?.deepCopy() ?: Bundle()
|
||||||
@ -309,13 +332,14 @@ class EditSearchActionSheetVM : ViewModel(), KoinComponent {
|
|||||||
it.replaceExtras(extras)
|
it.replaceExtras(extras)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
else -> action
|
else -> action
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun putIntExtra(key: String, value: Int = 0) {
|
fun putIntExtra(key: String, value: Int = 0) {
|
||||||
val action = searchAction.value ?: return
|
val action = searchAction.value ?: return
|
||||||
searchAction.value = when(action) {
|
searchAction.value = when (action) {
|
||||||
is CustomIntentActionBuilder -> action.copy(
|
is CustomIntentActionBuilder -> action.copy(
|
||||||
baseIntent = action.baseIntent.cloneFilter().also {
|
baseIntent = action.baseIntent.cloneFilter().also {
|
||||||
val extras = action.baseIntent.extras?.deepCopy() ?: Bundle()
|
val extras = action.baseIntent.extras?.deepCopy() ?: Bundle()
|
||||||
@ -323,6 +347,7 @@ class EditSearchActionSheetVM : ViewModel(), KoinComponent {
|
|||||||
it.replaceExtras(extras)
|
it.replaceExtras(extras)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
is AppSearchActionBuilder -> action.copy(
|
is AppSearchActionBuilder -> action.copy(
|
||||||
baseIntent = action.baseIntent.cloneFilter().also {
|
baseIntent = action.baseIntent.cloneFilter().also {
|
||||||
val extras = action.baseIntent.extras?.deepCopy() ?: Bundle()
|
val extras = action.baseIntent.extras?.deepCopy() ?: Bundle()
|
||||||
@ -330,13 +355,14 @@ class EditSearchActionSheetVM : ViewModel(), KoinComponent {
|
|||||||
it.replaceExtras(extras)
|
it.replaceExtras(extras)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
else -> action
|
else -> action
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun putLongExtra(key: String, value: Long = 0L) {
|
fun putLongExtra(key: String, value: Long = 0L) {
|
||||||
val action = searchAction.value ?: return
|
val action = searchAction.value ?: return
|
||||||
searchAction.value = when(action) {
|
searchAction.value = when (action) {
|
||||||
is CustomIntentActionBuilder -> action.copy(
|
is CustomIntentActionBuilder -> action.copy(
|
||||||
baseIntent = action.baseIntent.cloneFilter().also {
|
baseIntent = action.baseIntent.cloneFilter().also {
|
||||||
val extras = action.baseIntent.extras?.deepCopy() ?: Bundle()
|
val extras = action.baseIntent.extras?.deepCopy() ?: Bundle()
|
||||||
@ -344,6 +370,7 @@ class EditSearchActionSheetVM : ViewModel(), KoinComponent {
|
|||||||
it.replaceExtras(extras)
|
it.replaceExtras(extras)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
is AppSearchActionBuilder -> action.copy(
|
is AppSearchActionBuilder -> action.copy(
|
||||||
baseIntent = action.baseIntent.cloneFilter().also {
|
baseIntent = action.baseIntent.cloneFilter().also {
|
||||||
val extras = action.baseIntent.extras?.deepCopy() ?: Bundle()
|
val extras = action.baseIntent.extras?.deepCopy() ?: Bundle()
|
||||||
@ -351,13 +378,14 @@ class EditSearchActionSheetVM : ViewModel(), KoinComponent {
|
|||||||
it.replaceExtras(extras)
|
it.replaceExtras(extras)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
else -> action
|
else -> action
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun putFloatExtra(key: String, value: Float = 0f) {
|
fun putFloatExtra(key: String, value: Float = 0f) {
|
||||||
val action = searchAction.value ?: return
|
val action = searchAction.value ?: return
|
||||||
searchAction.value = when(action) {
|
searchAction.value = when (action) {
|
||||||
is CustomIntentActionBuilder -> action.copy(
|
is CustomIntentActionBuilder -> action.copy(
|
||||||
baseIntent = action.baseIntent.cloneFilter().also {
|
baseIntent = action.baseIntent.cloneFilter().also {
|
||||||
val extras = action.baseIntent.extras?.deepCopy() ?: Bundle()
|
val extras = action.baseIntent.extras?.deepCopy() ?: Bundle()
|
||||||
@ -365,6 +393,7 @@ class EditSearchActionSheetVM : ViewModel(), KoinComponent {
|
|||||||
it.replaceExtras(extras)
|
it.replaceExtras(extras)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
is AppSearchActionBuilder -> action.copy(
|
is AppSearchActionBuilder -> action.copy(
|
||||||
baseIntent = action.baseIntent.cloneFilter().also {
|
baseIntent = action.baseIntent.cloneFilter().also {
|
||||||
val extras = action.baseIntent.extras?.deepCopy() ?: Bundle()
|
val extras = action.baseIntent.extras?.deepCopy() ?: Bundle()
|
||||||
@ -372,13 +401,14 @@ class EditSearchActionSheetVM : ViewModel(), KoinComponent {
|
|||||||
it.replaceExtras(extras)
|
it.replaceExtras(extras)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
else -> action
|
else -> action
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun putDoubleExtra(key: String, value: Double = 0.0) {
|
fun putDoubleExtra(key: String, value: Double = 0.0) {
|
||||||
val action = searchAction.value ?: return
|
val action = searchAction.value ?: return
|
||||||
searchAction.value = when(action) {
|
searchAction.value = when (action) {
|
||||||
is CustomIntentActionBuilder -> action.copy(
|
is CustomIntentActionBuilder -> action.copy(
|
||||||
baseIntent = action.baseIntent.cloneFilter().also {
|
baseIntent = action.baseIntent.cloneFilter().also {
|
||||||
val extras = action.baseIntent.extras?.deepCopy() ?: Bundle()
|
val extras = action.baseIntent.extras?.deepCopy() ?: Bundle()
|
||||||
@ -386,6 +416,7 @@ class EditSearchActionSheetVM : ViewModel(), KoinComponent {
|
|||||||
it.replaceExtras(extras)
|
it.replaceExtras(extras)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
is AppSearchActionBuilder -> action.copy(
|
is AppSearchActionBuilder -> action.copy(
|
||||||
baseIntent = action.baseIntent.cloneFilter().also {
|
baseIntent = action.baseIntent.cloneFilter().also {
|
||||||
val extras = action.baseIntent.extras?.deepCopy() ?: Bundle()
|
val extras = action.baseIntent.extras?.deepCopy() ?: Bundle()
|
||||||
@ -393,13 +424,14 @@ class EditSearchActionSheetVM : ViewModel(), KoinComponent {
|
|||||||
it.replaceExtras(extras)
|
it.replaceExtras(extras)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
else -> action
|
else -> action
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun putBooleanExtra(key: String, value: Boolean = false) {
|
fun putBooleanExtra(key: String, value: Boolean = false) {
|
||||||
val action = searchAction.value ?: return
|
val action = searchAction.value ?: return
|
||||||
searchAction.value = when(action) {
|
searchAction.value = when (action) {
|
||||||
is CustomIntentActionBuilder -> action.copy(
|
is CustomIntentActionBuilder -> action.copy(
|
||||||
baseIntent = action.baseIntent.cloneFilter().also {
|
baseIntent = action.baseIntent.cloneFilter().also {
|
||||||
val extras = action.baseIntent.extras?.deepCopy() ?: Bundle()
|
val extras = action.baseIntent.extras?.deepCopy() ?: Bundle()
|
||||||
@ -407,6 +439,7 @@ class EditSearchActionSheetVM : ViewModel(), KoinComponent {
|
|||||||
it.replaceExtras(extras)
|
it.replaceExtras(extras)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
is AppSearchActionBuilder -> action.copy(
|
is AppSearchActionBuilder -> action.copy(
|
||||||
baseIntent = action.baseIntent.cloneFilter().also {
|
baseIntent = action.baseIntent.cloneFilter().also {
|
||||||
val extras = action.baseIntent.extras?.deepCopy() ?: Bundle()
|
val extras = action.baseIntent.extras?.deepCopy() ?: Bundle()
|
||||||
@ -414,13 +447,14 @@ class EditSearchActionSheetVM : ViewModel(), KoinComponent {
|
|||||||
it.replaceExtras(extras)
|
it.replaceExtras(extras)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
else -> action
|
else -> action
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setIntentAction(action: String) {
|
fun setIntentAction(action: String) {
|
||||||
val searchAction = searchAction.value ?: return
|
val searchAction = searchAction.value ?: return
|
||||||
this.searchAction.value = when(searchAction) {
|
this.searchAction.value = when (searchAction) {
|
||||||
is CustomIntentActionBuilder -> searchAction.copy(
|
is CustomIntentActionBuilder -> searchAction.copy(
|
||||||
baseIntent = searchAction.baseIntent.cloneFilter().also {
|
baseIntent = searchAction.baseIntent.cloneFilter().also {
|
||||||
val extras = searchAction.baseIntent.extras?.deepCopy() ?: Bundle()
|
val extras = searchAction.baseIntent.extras?.deepCopy() ?: Bundle()
|
||||||
@ -428,13 +462,14 @@ class EditSearchActionSheetVM : ViewModel(), KoinComponent {
|
|||||||
it.action = action
|
it.action = action
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
else -> searchAction
|
else -> searchAction
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setIntentCategory(category: String) {
|
fun setIntentCategory(category: String) {
|
||||||
val action = searchAction.value ?: return
|
val action = searchAction.value ?: return
|
||||||
searchAction.value = when(action) {
|
searchAction.value = when (action) {
|
||||||
is CustomIntentActionBuilder -> action.copy(
|
is CustomIntentActionBuilder -> action.copy(
|
||||||
baseIntent = action.baseIntent.cloneFilter().also {
|
baseIntent = action.baseIntent.cloneFilter().also {
|
||||||
val extras = action.baseIntent.extras?.deepCopy() ?: Bundle()
|
val extras = action.baseIntent.extras?.deepCopy() ?: Bundle()
|
||||||
@ -444,26 +479,29 @@ class EditSearchActionSheetVM : ViewModel(), KoinComponent {
|
|||||||
if (category.isNotBlank()) it.addCategory(category)
|
if (category.isNotBlank()) it.addCategory(category)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
else -> action
|
else -> action
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setIntentQueryExtra(key: String) {
|
fun setIntentQueryExtra(key: String) {
|
||||||
val action = searchAction.value ?: return
|
val action = searchAction.value ?: return
|
||||||
searchAction.value = when(action) {
|
searchAction.value = when (action) {
|
||||||
is CustomIntentActionBuilder -> action.copy(
|
is CustomIntentActionBuilder -> action.copy(
|
||||||
queryKey = key,
|
queryKey = key,
|
||||||
)
|
)
|
||||||
|
|
||||||
else -> action
|
else -> action
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setQueryEncoding(encoding: WebsearchActionBuilder.QueryEncoding) {
|
fun setQueryEncoding(encoding: WebsearchActionBuilder.QueryEncoding) {
|
||||||
val action = searchAction.value ?: return
|
val action = searchAction.value ?: return
|
||||||
searchAction.value = when(action) {
|
searchAction.value = when (action) {
|
||||||
is WebsearchActionBuilder -> action.copy(
|
is WebsearchActionBuilder -> action.copy(
|
||||||
encoding = encoding
|
encoding = encoding
|
||||||
)
|
)
|
||||||
|
|
||||||
else -> action
|
else -> action
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,10 +16,12 @@ import de.mm20.launcher2.search.data.LauncherApp
|
|||||||
import kotlinx.collections.immutable.ImmutableList
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
import kotlinx.collections.immutable.toImmutableList
|
import kotlinx.collections.immutable.toImmutableList
|
||||||
import kotlinx.coroutines.Dispatchers
|
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 kotlinx.coroutines.withContext
|
||||||
import org.apache.commons.text.similarity.FuzzyScore
|
import org.apache.commons.text.similarity.FuzzyScore
|
||||||
import java.util.*
|
import java.util.Locale
|
||||||
|
|
||||||
interface AppRepository {
|
interface AppRepository {
|
||||||
fun getAllInstalledApps(): Flow<List<LauncherApp>>
|
fun getAllInstalledApps(): Flow<List<LauncherApp>>
|
||||||
@ -141,9 +143,8 @@ internal class AppRepositoryImpl(
|
|||||||
return LauncherApp(context, launcherActivityInfo)
|
return LauncherApp(context, launcherActivityInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun search(query: String): Flow<ImmutableList<LauncherApp>> = channelFlow {
|
override fun search(query: String): Flow<ImmutableList<LauncherApp>> {
|
||||||
|
return installedApps.map { apps ->
|
||||||
installedApps.collectLatest { apps ->
|
|
||||||
withContext(Dispatchers.Default) {
|
withContext(Dispatchers.Default) {
|
||||||
val appResults = mutableListOf<LauncherApp>()
|
val appResults = mutableListOf<LauncherApp>()
|
||||||
if (query.isEmpty()) {
|
if (query.isEmpty()) {
|
||||||
@ -156,10 +157,8 @@ internal class AppRepositoryImpl(
|
|||||||
val componentName = ComponentName.unflattenFromString(query)
|
val componentName = ComponentName.unflattenFromString(query)
|
||||||
getActivityByComponentName(componentName)?.let { appResults.add(it) }
|
getActivityByComponentName(componentName)?.let { appResults.add(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
appResults.sort()
|
appResults.sort()
|
||||||
|
appResults.toImmutableList()
|
||||||
send(appResults.toImmutableList())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -21,8 +21,7 @@ import kotlinx.collections.immutable.persistentListOf
|
|||||||
import kotlinx.collections.immutable.toImmutableList
|
import kotlinx.collections.immutable.toImmutableList
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.emitAll
|
import kotlinx.coroutines.flow.flowOf
|
||||||
import kotlinx.coroutines.flow.flow
|
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
@ -37,7 +36,7 @@ import java.net.URL
|
|||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
||||||
interface SearchActionService {
|
interface SearchActionService {
|
||||||
fun search(query: String): Flow<ImmutableList<SearchAction>>
|
suspend fun search(query: String): Flow<ImmutableList<SearchAction>>
|
||||||
|
|
||||||
fun getSearchActionBuilders(): Flow<List<SearchActionBuilder>>
|
fun getSearchActionBuilders(): Flow<List<SearchActionBuilder>>
|
||||||
fun getDisabledActionBuilders(): Flow<List<SearchActionBuilder>>
|
fun getDisabledActionBuilders(): Flow<List<SearchActionBuilder>>
|
||||||
@ -56,23 +55,21 @@ internal class SearchActionServiceImpl(
|
|||||||
private val repository: SearchActionRepository,
|
private val repository: SearchActionRepository,
|
||||||
private val textClassifier: TextClassifier,
|
private val textClassifier: TextClassifier,
|
||||||
) : SearchActionService {
|
) : SearchActionService {
|
||||||
override fun search(
|
override suspend fun search(
|
||||||
query: String
|
query: String
|
||||||
): Flow<ImmutableList<SearchAction>> = flow {
|
): Flow<ImmutableList<SearchAction>> {
|
||||||
|
|
||||||
if (query.isBlank()) {
|
if (query.isBlank()) {
|
||||||
emit(persistentListOf())
|
return flowOf(persistentListOf())
|
||||||
return@flow
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val classificationResult = textClassifier.classify(context, query)
|
val classificationResult = textClassifier.classify(context, query)
|
||||||
|
|
||||||
val builders = repository.getSearchActionBuilders()
|
val builders = repository.getSearchActionBuilders()
|
||||||
|
|
||||||
emitAll(
|
return builders.map {
|
||||||
builders.map {
|
it.mapNotNull { it.build(context, classificationResult) }.toImmutableList()
|
||||||
it.mapNotNull { it.build(context, classificationResult) }.toImmutableList()
|
}
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getSearchActionBuilders(): Flow<List<SearchActionBuilder>> {
|
override fun getSearchActionBuilders(): Flow<List<SearchActionBuilder>> {
|
||||||
@ -136,7 +133,10 @@ internal class SearchActionServiceImpl(
|
|||||||
return@withContext null
|
return@withContext null
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun importOpenSearch(openSearchHref: String, iconSize: Int): WebsearchActionBuilder? {
|
private suspend fun importOpenSearch(
|
||||||
|
openSearchHref: String,
|
||||||
|
iconSize: Int
|
||||||
|
): WebsearchActionBuilder? {
|
||||||
try {
|
try {
|
||||||
val httpClient = OkHttpClient()
|
val httpClient = OkHttpClient()
|
||||||
val request = Request.Builder()
|
val request = Request.Builder()
|
||||||
@ -217,7 +217,8 @@ internal class SearchActionServiceImpl(
|
|||||||
}
|
}
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
|
|
||||||
} catch (e: XmlPullParserException) {}
|
} catch (e: XmlPullParserException) {
|
||||||
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -242,7 +243,8 @@ internal class SearchActionServiceImpl(
|
|||||||
override suspend fun getSearchActivities(): List<ComponentName> {
|
override suspend fun getSearchActivities(): List<ComponentName> {
|
||||||
return withContext(Dispatchers.Default) {
|
return withContext(Dispatchers.Default) {
|
||||||
val resolveInfos = context.packageManager.queryIntentActivities(
|
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 {
|
resolveInfos.mapNotNull {
|
||||||
if (!it.activityInfo.exported || !it.activityInfo.enabled) return@mapNotNull null
|
if (!it.activityInfo.exported || !it.activityInfo.enabled) return@mapNotNull null
|
||||||
|
|||||||
@ -51,7 +51,7 @@ class IconPackManager(
|
|||||||
}
|
}
|
||||||
|
|
||||||
suspend fun updateIconPacks() {
|
suspend fun updateIconPacks() {
|
||||||
withContext(Dispatchers.Default) {
|
withContext(Dispatchers.IO) {
|
||||||
IconPackInstaller(context, appDatabase).installIcons()
|
IconPackInstaller(context, appDatabase).installIcons()
|
||||||
GrayscaleMapInstaller(context, appDatabase).installIcons()
|
GrayscaleMapInstaller(context, appDatabase).installIcons()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -30,8 +30,8 @@ import de.mm20.launcher2.search.data.OwncloudFile
|
|||||||
import de.mm20.launcher2.search.data.UnitConverter
|
import de.mm20.launcher2.search.data.UnitConverter
|
||||||
import de.mm20.launcher2.search.data.Website
|
import de.mm20.launcher2.search.data.Website
|
||||||
import de.mm20.launcher2.search.data.Wikipedia
|
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.SearchActionService
|
||||||
|
import de.mm20.launcher2.searchactions.actions.SearchAction
|
||||||
import de.mm20.launcher2.unitconverter.UnitConverterRepository
|
import de.mm20.launcher2.unitconverter.UnitConverterRepository
|
||||||
import de.mm20.launcher2.websites.WebsiteRepository
|
import de.mm20.launcher2.websites.WebsiteRepository
|
||||||
import de.mm20.launcher2.wikipedia.WikipediaRepository
|
import de.mm20.launcher2.wikipedia.WikipediaRepository
|
||||||
@ -58,7 +58,7 @@ interface SearchService {
|
|||||||
unitConverter: UnitConverterSearchSettings,
|
unitConverter: UnitConverterSearchSettings,
|
||||||
websites: WebsiteSearchSettings,
|
websites: WebsiteSearchSettings,
|
||||||
wikipedia: WikipediaSearchSettings,
|
wikipedia: WikipediaSearchSettings,
|
||||||
): Flow<ImmutableList<Searchable>>
|
): Flow<SearchResults>
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class SearchServiceImpl(
|
internal class SearchServiceImpl(
|
||||||
@ -85,16 +85,23 @@ internal class SearchServiceImpl(
|
|||||||
unitConverter: UnitConverterSearchSettings,
|
unitConverter: UnitConverterSearchSettings,
|
||||||
websites: WebsiteSearchSettings,
|
websites: WebsiteSearchSettings,
|
||||||
wikipedia: WikipediaSearchSettings,
|
wikipedia: WikipediaSearchSettings,
|
||||||
): Flow<ImmutableList<Searchable>> = channelFlow {
|
): Flow<SearchResults> = channelFlow {
|
||||||
var searchActionsReady = false
|
val results = MutableStateFlow(SearchResults())
|
||||||
supervisorScope {
|
supervisorScope {
|
||||||
val results = MutableStateFlow(SearchResults())
|
launch {
|
||||||
|
searchActionService.search(query)
|
||||||
|
.collectLatest { r ->
|
||||||
|
results.update {
|
||||||
|
it.copy(searchActions = r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
launch {
|
launch {
|
||||||
appRepository.search(query)
|
appRepository.search(query)
|
||||||
.withCustomLabels(customAttributesRepository)
|
.withCustomLabels(customAttributesRepository)
|
||||||
.collectLatest { r ->
|
.collectLatest { r ->
|
||||||
results.update {
|
results.update {
|
||||||
it.copy(apps = r)
|
it.copy(apps = r.toImmutableList())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -104,7 +111,7 @@ internal class SearchServiceImpl(
|
|||||||
.withCustomLabels(customAttributesRepository)
|
.withCustomLabels(customAttributesRepository)
|
||||||
.collectLatest { r ->
|
.collectLatest { r ->
|
||||||
results.update {
|
results.update {
|
||||||
it.copy(shortcuts = r)
|
it.copy(shortcuts = r.toImmutableList())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -115,7 +122,7 @@ internal class SearchServiceImpl(
|
|||||||
.withCustomLabels(customAttributesRepository)
|
.withCustomLabels(customAttributesRepository)
|
||||||
.collectLatest { r ->
|
.collectLatest { r ->
|
||||||
results.update {
|
results.update {
|
||||||
it.copy(contacts = r)
|
it.copy(contacts = r.toImmutableList())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -126,7 +133,7 @@ internal class SearchServiceImpl(
|
|||||||
.withCustomLabels(customAttributesRepository)
|
.withCustomLabels(customAttributesRepository)
|
||||||
.collectLatest { r ->
|
.collectLatest { r ->
|
||||||
results.update {
|
results.update {
|
||||||
it.copy(calendars = r)
|
it.copy(calendars = r.toImmutableList())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -143,12 +150,13 @@ internal class SearchServiceImpl(
|
|||||||
}
|
}
|
||||||
if (unitConverter.enabled) {
|
if (unitConverter.enabled) {
|
||||||
launch {
|
launch {
|
||||||
unitConverterRepository.search(query, unitConverter.currencies).collectLatest { r ->
|
unitConverterRepository.search(query, unitConverter.currencies)
|
||||||
results.update {
|
.collectLatest { r ->
|
||||||
it.copy(unitConverters = r?.let { persistentListOf(it) }
|
results.update {
|
||||||
?: persistentListOf())
|
it.copy(unitConverters = r?.let { persistentListOf(it) }
|
||||||
|
?: persistentListOf())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (websites.enabled) {
|
if (websites.enabled) {
|
||||||
@ -158,7 +166,7 @@ internal class SearchServiceImpl(
|
|||||||
.withCustomLabels(customAttributesRepository)
|
.withCustomLabels(customAttributesRepository)
|
||||||
.collectLatest { r ->
|
.collectLatest { r ->
|
||||||
results.update {
|
results.update {
|
||||||
it.copy(websites = r)
|
it.copy(websites = r.toImmutableList())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -170,7 +178,7 @@ internal class SearchServiceImpl(
|
|||||||
.withCustomLabels(customAttributesRepository)
|
.withCustomLabels(customAttributesRepository)
|
||||||
.collectLatest { r ->
|
.collectLatest { r ->
|
||||||
results.update {
|
results.update {
|
||||||
it.copy(wikipedia = r)
|
it.copy(wikipedia = r.toImmutableList())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -188,7 +196,7 @@ internal class SearchServiceImpl(
|
|||||||
.withCustomLabels(customAttributesRepository)
|
.withCustomLabels(customAttributesRepository)
|
||||||
.collectLatest { r ->
|
.collectLatest { r ->
|
||||||
results.update {
|
results.update {
|
||||||
it.copy(files = r)
|
it.copy(files = r.toImmutableList())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -218,39 +226,23 @@ internal class SearchServiceImpl(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
launch {
|
launch {
|
||||||
searchActionService.search(query)
|
results.collectLatest { send(it) }
|
||||||
.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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal data class SearchResults(
|
data class SearchResults(
|
||||||
val apps: List<LauncherApp> = emptyList(),
|
val apps: ImmutableList<LauncherApp>? = null,
|
||||||
val shortcuts: List<AppShortcut> = emptyList(),
|
val shortcuts: ImmutableList<AppShortcut>? = null,
|
||||||
val contacts: List<Contact> = emptyList(),
|
val contacts: ImmutableList<Contact>? = null,
|
||||||
val calendars: List<CalendarEvent> = emptyList(),
|
val calendars: ImmutableList<CalendarEvent>? = null,
|
||||||
val files: List<File> = emptyList(),
|
val files: ImmutableList<File>? = null,
|
||||||
val calculators: List<Calculator> = emptyList(),
|
val calculators: ImmutableList<Calculator>? = null,
|
||||||
val unitConverters: List<UnitConverter> = emptyList(),
|
val unitConverters: ImmutableList<UnitConverter>? = null,
|
||||||
val websites: List<Website> = emptyList(),
|
val websites: ImmutableList<Website>? = null,
|
||||||
val wikipedia: List<Wikipedia> = emptyList(),
|
val wikipedia: ImmutableList<Wikipedia>? = null,
|
||||||
val searchActions: List<SearchAction> = emptyList(),
|
val searchActions: ImmutableList<SearchAction>? = null,
|
||||||
val other: List<SavableSearchable> = emptyList(),
|
val other: ImmutableList<SavableSearchable>? = null,
|
||||||
) {
|
)
|
||||||
fun toList(): List<Searchable> {
|
|
||||||
return searchActions + (apps + shortcuts + contacts + calendars + files + websites + wikipedia + other).distinctBy { it.key } + calculators + unitConverters
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
x
Reference in New Issue
Block a user