Optimizations
This commit is contained in:
parent
722652e982
commit
e5fe3a8816
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -51,7 +51,7 @@ class IconPackManager(
|
||||
}
|
||||
|
||||
suspend fun updateIconPacks() {
|
||||
withContext(Dispatchers.Default) {
|
||||
withContext(Dispatchers.IO) {
|
||||
IconPackInstaller(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.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,
|
||||
)
|
||||
Loading…
x
Reference in New Issue
Block a user