From 02c2b3fd8d66a6c307b505843f3289bb5b7255a6 Mon Sep 17 00:00:00 2001 From: Christoph Liebender Date: Fri, 24 May 2024 19:28:37 +0200 Subject: [PATCH 1/4] Fix: don't treat PH OpeningHours with specified time as `everyDay` --- .../java/de/mm20/launcher2/openstreetmaps/OsmLocation.kt | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/data/openstreetmaps/src/main/java/de/mm20/launcher2/openstreetmaps/OsmLocation.kt b/data/openstreetmaps/src/main/java/de/mm20/launcher2/openstreetmaps/OsmLocation.kt index 4f10ec801..ed6fbc157 100644 --- a/data/openstreetmaps/src/main/java/de/mm20/launcher2/openstreetmaps/OsmLocation.kt +++ b/data/openstreetmaps/src/main/java/de/mm20/launcher2/openstreetmaps/OsmLocation.kt @@ -239,8 +239,12 @@ internal fun parseOpeningSchedule(it: String?): OpeningSchedule? { // if no day specified, treat as "every day" if (days.isEmpty()) { - everyDay = true - days = daysOfWeek.toSet() + if (group.any { it.equals("PH", ignoreCase = true) }) { + times = emptyList() + } else { + everyDay = true + days = daysOfWeek.toSet() + } } openingHours.addAll(days.flatMap { day -> From 0e340d4408a852026fed5748e49726605fe59039 Mon Sep 17 00:00:00 2001 From: Christoph Liebender Date: Sat, 26 Oct 2024 17:43:16 +0200 Subject: [PATCH 2/4] Use SnapshotStateList in SearchVM and don't clear search-results if there are no new search results from SearchableRepositories --- .../ui/assistant/AssistantScaffold.kt | 4 +- .../launcher2/ui/launcher/PagerScaffold.kt | 10 +- .../launcher2/ui/launcher/PullDownScaffold.kt | 37 +-- .../ui/launcher/search/SearchColumn.kt | 27 +- .../launcher2/ui/launcher/search/SearchVM.kt | 244 ++++++++---------- .../launcher/searchbar/LauncherSearchBar.kt | 2 +- .../launcher2/search/SearchableRepository.kt | 1 - .../de/mm20/launcher2/search/SearchService.kt | 4 +- 8 files changed, 145 insertions(+), 184 deletions(-) diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/assistant/AssistantScaffold.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/assistant/AssistantScaffold.kt index cd961a1eb..c7cfd97a1 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/assistant/AssistantScaffold.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/assistant/AssistantScaffold.kt @@ -147,7 +147,7 @@ fun AssistantScaffold( val density = LocalDensity.current val maxSearchBarOffset = with(density) { 128.dp.toPx() } var searchBarOffset by remember { - mutableStateOf(0f) + mutableFloatStateOf(0f) } val nestedScrollConnection = remember { @@ -159,7 +159,7 @@ fun AssistantScaffold( } } } - val actions by searchVM.searchActionResults + val actions = searchVM.searchActionResults val webSearchPadding by animateDpAsState( if (actions.isEmpty()) 0.dp else 48.dp ) diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/PagerScaffold.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/PagerScaffold.kt index 831a93a86..99fa329ea 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/PagerScaffold.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/PagerScaffold.kt @@ -51,7 +51,7 @@ import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.mutableFloatStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier @@ -124,7 +124,7 @@ fun PagerScaffold( val isSearchOpen by viewModel.isSearchOpen val isWidgetEditMode by viewModel.isWidgetEditMode - val actions by searchVM.searchActionResults + val actions = searchVM.searchActionResults val widgetsScrollState = rememberScrollState() val searchState = rememberLazyListState() @@ -272,7 +272,7 @@ fun PagerScaffold( } } - val searchBarOffset = remember { mutableStateOf(0f) } + val searchBarOffset = remember { mutableFloatStateOf(0f) } val scope = rememberCoroutineScope() @@ -341,8 +341,8 @@ fun PagerScaffold( } val deltaSearchBarOffset = consumed.y * if (isSearchOpen && reverseSearchResults) 1 else -1 - searchBarOffset.value = - (searchBarOffset.value + deltaSearchBarOffset).coerceIn(0f, maxSearchBarOffset) + searchBarOffset.floatValue = + (searchBarOffset.floatValue + deltaSearchBarOffset).coerceIn(0f, maxSearchBarOffset) return super.onPostScroll(consumed, available, source) } diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/PullDownScaffold.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/PullDownScaffold.kt index 9f043e4b4..7e765b1b8 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/PullDownScaffold.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/PullDownScaffold.kt @@ -47,6 +47,7 @@ import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableFloatStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope @@ -106,7 +107,7 @@ fun PullDownScaffold( val density = LocalDensity.current val context = LocalContext.current - val actions by searchVM.searchActionResults + val actions = searchVM.searchActionResults val isSearchOpen by viewModel.isSearchOpen val isWidgetEditMode by viewModel.isWidgetEditMode @@ -116,7 +117,7 @@ fun PullDownScaffold( val pagerState = rememberPagerState { 2 } - val offsetY = remember { mutableStateOf(0f) } + val offsetY = remember { mutableFloatStateOf(0f) } val maxOffset = with(density) { 64.dp.toPx() } val toggleSearchThreshold = with(density) { 48.dp.toPx() } @@ -153,13 +154,13 @@ fun PullDownScaffold( val isOverThreshold by remember { derivedStateOf { - offsetY.value.absoluteValue > toggleSearchThreshold + offsetY.floatValue.absoluteValue > toggleSearchThreshold } } val dragProgress by remember { derivedStateOf { - (offsetY.value.absoluteValue / toggleSearchThreshold).coerceAtMost(1f) + (offsetY.floatValue.absoluteValue / toggleSearchThreshold).coerceAtMost(1f) } } @@ -236,7 +237,7 @@ fun PullDownScaffold( } } - val searchBarOffset = remember { mutableStateOf(0f) } + val searchBarOffset = remember { mutableFloatStateOf(0f) } val maxSearchBarOffset = with(density) { 128.dp.toPx() } @@ -281,7 +282,7 @@ fun PullDownScaffold( } LaunchedEffect(isWidgetEditMode) { - if (!isWidgetEditMode) searchBarOffset.value = 0f + if (!isWidgetEditMode) searchBarOffset.floatValue = 0f } val handleBackOrHomeEvent = { @@ -321,7 +322,7 @@ fun PullDownScaffold( LaunchedEffect(isOverThreshold) { if (isOverThreshold) { view.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY) - } else if (offsetY.value != 0f) { + } else if (offsetY.floatValue != 0f) { view.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY_RELEASE) } } @@ -342,15 +343,15 @@ fun PullDownScaffold( val canPullUp = isSearchOpen && isSearchAtBottom val consumed = when { - canPullUp && available.y < 0 || offsetY.value < 0 -> { + canPullUp && available.y < 0 || offsetY.floatValue < 0 -> { val consumed = available.y - offsetY.value = (offsetY.value + (consumed * 0.5f)).coerceIn(-maxOffset, 0f) + offsetY.floatValue = (offsetY.floatValue + (consumed * 0.5f)).coerceIn(-maxOffset, 0f) consumed } - canPullDown && available.y > 0 || offsetY.value > 0 -> { + canPullDown && available.y > 0 || offsetY.floatValue > 0 -> { val consumed = available.y - offsetY.value = (offsetY.value + (consumed * 0.5f)).coerceIn(0f, maxOffset) + offsetY.floatValue = (offsetY.floatValue + (consumed * 0.5f)).coerceIn(0f, maxOffset) consumed } @@ -367,17 +368,17 @@ fun PullDownScaffold( ): Offset { val deltaSearchBarOffset = consumed.y * if (isSearchOpen && reverseSearchResults) 1 else -1 - searchBarOffset.value = - (searchBarOffset.value + deltaSearchBarOffset).coerceIn(0f, maxSearchBarOffset) + searchBarOffset.floatValue = + (searchBarOffset.floatValue + deltaSearchBarOffset).coerceIn(0f, maxSearchBarOffset) return super.onPostScroll(consumed, available, source) } override suspend fun onPreFling(available: Velocity): Velocity { - if (offsetY.value > toggleSearchThreshold || offsetY.value < -toggleSearchThreshold) { + if (offsetY.floatValue > toggleSearchThreshold || offsetY.floatValue < -toggleSearchThreshold) { viewModel.toggleSearch() } if (!isWidgetEditMode) gestureManager.dispatchDragEnd() - if (offsetY.value != 0f) { + if (offsetY.floatValue != 0f) { offsetY.animateTo(0f) return available } @@ -406,7 +407,7 @@ fun PullDownScaffold( } ) } - .offset { IntOffset(0, offsetY.value.toInt()) }, + .offset { IntOffset(0, offsetY.floatValue.toInt()) }, contentAlignment = Alignment.TopCenter ) { BoxWithConstraints( @@ -576,7 +577,7 @@ fun PullDownScaffold( val searchBarLevel by remember { derivedStateOf { when { - offsetY.value != 0f -> SearchBarLevel.Raised + offsetY.floatValue != 0f -> SearchBarLevel.Raised !isSearchOpen && isWidgetsAtStart && (fillClockHeight || !bottomSearchBar) -> SearchBarLevel.Resting isSearchOpen && isSearchAtTop && !bottomSearchBar -> SearchBarLevel.Active isSearchOpen && isSearchAtBottom && bottomSearchBar -> SearchBarLevel.Active @@ -613,7 +614,7 @@ fun PullDownScaffold( darkColors = LocalPreferDarkContentOverWallpaper.current && searchBarColor == SearchBarColors.Auto || searchBarColor == SearchBarColors.Dark, bottomSearchBar = bottomSearchBar, searchBarOffset = { - (if (searchBarFocused || fixedSearchBar) 0 else searchBarOffset.value.toInt() * (if (bottomSearchBar) 1 else -1)) + + (if (searchBarFocused || fixedSearchBar) 0 else searchBarOffset.floatValue.toInt() * (if (bottomSearchBar) 1 else -1)) + with(density) { (editModeSearchBarOffset - if (bottomSearchBar) keyboardFilterBarPadding else 0.dp) .toPx() diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/SearchColumn.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/SearchColumn.kt index 70928ab46..091d1c7c5 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/SearchColumn.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/SearchColumn.kt @@ -1,6 +1,5 @@ package de.mm20.launcher2.ui.launcher.search -import android.util.Log import androidx.activity.compose.BackHandler import androidx.appcompat.app.AppCompatActivity import androidx.compose.animation.AnimatedContent @@ -75,22 +74,22 @@ fun SearchColumn( val hideFavs by viewModel.hideFavorites val favoritesEnabled by viewModel.favoritesEnabled.collectAsState(false) - val apps by viewModel.appResults - val workApps by viewModel.workAppResults - val privateApps by viewModel.privateSpaceAppResults + val apps = viewModel.appResults + val workApps = viewModel.workAppResults + val privateApps = viewModel.privateSpaceAppResults val profiles by viewModel.profiles.collectAsState(emptyList()) val profileStates by viewModel.profileStates.collectAsState(emptyList()) - val appShortcuts by viewModel.appShortcutResults - val contacts by viewModel.contactResults - val files by viewModel.fileResults - val events by viewModel.calendarResults - val unitConverter by viewModel.unitConverterResults - val calculator by viewModel.calculatorResults - val wikipedia by viewModel.articleResults - val locations by viewModel.locationResults - val website by viewModel.websiteResults - val hiddenResults by viewModel.hiddenResults + val appShortcuts = viewModel.appShortcutResults + val contacts = viewModel.contactResults + val files = viewModel.fileResults + val events = viewModel.calendarResults + val unitConverter = viewModel.unitConverterResults + val calculator = viewModel.calculatorResults + val wikipedia = viewModel.articleResults + val locations = viewModel.locationResults + val website = viewModel.websiteResults + val hiddenResults = viewModel.hiddenResults val bestMatch by viewModel.bestMatch diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/SearchVM.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/SearchVM.kt index d96e6c11f..c9da67edd 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/SearchVM.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/SearchVM.kt @@ -2,7 +2,9 @@ package de.mm20.launcher2.ui.launcher.search import android.content.Context import androidx.appcompat.app.AppCompatActivity +import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.snapshots.SnapshotStateList import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import de.mm20.launcher2.devicepose.DevicePoseProvider @@ -28,6 +30,7 @@ import de.mm20.launcher2.search.Location import de.mm20.launcher2.search.ResultScore import de.mm20.launcher2.search.SavableSearchable import de.mm20.launcher2.search.SearchFilters +import de.mm20.launcher2.search.SearchResults import de.mm20.launcher2.search.SearchService import de.mm20.launcher2.search.Searchable import de.mm20.launcher2.search.Website @@ -79,8 +82,6 @@ class SearchVM : ViewModel(), KoinComponent { val expandedCategory = mutableStateOf(null) - val locationResults = mutableStateOf>(emptyList()) - val profiles = profileManager.profiles.shareIn( viewModelScope, SharingStarted.WhileSubscribed(), @@ -104,22 +105,36 @@ class SearchVM : ViewModel(), KoinComponent { } } - val appResults = mutableStateOf>(emptyList()) - val workAppResults = mutableStateOf>(emptyList()) - val privateSpaceAppResults = mutableStateOf>(emptyList()) - - val appShortcutResults = mutableStateOf>(emptyList()) - val fileResults = mutableStateOf>(emptyList()) - val contactResults = mutableStateOf>(emptyList()) - val calendarResults = mutableStateOf>(emptyList()) - val articleResults = mutableStateOf>(emptyList()) - val websiteResults = mutableStateOf>(emptyList()) - val calculatorResults = mutableStateOf>(emptyList()) - val unitConverterResults = mutableStateOf>(emptyList()) - val searchActionResults = mutableStateOf>(emptyList()) + val appResults = mutableStateListOf() + val workAppResults = mutableStateListOf() + val privateSpaceAppResults = mutableStateListOf() + + val appShortcutResults = mutableStateListOf() + val fileResults = mutableStateListOf() + val contactResults = mutableStateListOf() + val calendarResults = mutableStateListOf() + val articleResults = mutableStateListOf
() + val websiteResults = mutableStateListOf() + val calculatorResults = mutableStateListOf() + val unitConverterResults = mutableStateListOf() + val searchActionResults = mutableStateListOf() + val locationResults = mutableStateListOf() + + var previousResults: SearchResults? = null + + private fun mergeStateLists(state: SnapshotStateList, newItems: List) { + val diff = state.toSet() subtract newItems.toSet() + state.removeAll(diff) + for ((i, item) in newItems.withIndex()) { + if (i < state.size) + state[i] = item + else + state.add(item) + } + } val hiddenResultsButton = searchUiSettings.hiddenItemsButton - val hiddenResults = mutableStateOf>(emptyList()) + val hiddenResults = mutableStateListOf() val favoritesEnabled = searchUiSettings.favorites val hideFavorites = mutableStateOf(false) @@ -179,7 +194,6 @@ class SearchVM : ViewModel(), KoinComponent { } searchQuery.value = query isSearchEmpty.value = query.isEmpty() - hiddenResults.value = emptyList() val filters = filters.value @@ -218,15 +232,6 @@ class SearchVM : ViewModel(), KoinComponent { flowOf(emptyList()) } val allApps = searchService.getAllApps() - fileResults.value = emptyList() - contactResults.value = emptyList() - calendarResults.value = emptyList() - locationResults.value = emptyList() - articleResults.value = emptyList() - websiteResults.value = emptyList() - calculatorResults.value = emptyList() - unitConverterResults.value = emptyList() - searchActionResults.value = emptyList() allApps .combine(hiddenItemKeys) { results, hiddenKeys -> results to hiddenKeys } @@ -254,10 +259,11 @@ class SearchVM : ViewModel(), KoinComponent { } hiddenItems += hiddenPrivateApps - appResults.value = apps - workAppResults.value = workApps - privateSpaceAppResults.value = privateApps - hiddenResults.value = hiddenItems + searchActionResults.clear() + mergeStateLists(appResults, apps) + mergeStateLists(workAppResults, workApps) + mergeStateLists(privateSpaceAppResults, privateApps) + mergeStateLists(hiddenResults, hiddenItems) } } else { @@ -267,127 +273,81 @@ class SearchVM : ViewModel(), KoinComponent { searchService.search( query, filters = if (query.isEmpty()) filters.copy(apps = true) else filters, + previousResults, ) .combine(hiddenItemKeys) { results, hiddenKeys -> results to hiddenKeys } .collectLatest { (results, hiddenKeys) -> - val hiddenItems = mutableListOf() - - if (results.apps != null) { - val (hiddenApps, apps) = results.apps!!.partition { - hiddenKeys.contains( - it.key - ) - } - hiddenItems += hiddenApps - appResults.value = apps.applyRanking(query) - } else { - appResults.value = emptyList() - } - workAppResults.value = emptyList() - privateSpaceAppResults.value = emptyList() - - if (results.shortcuts != null) { - val (hiddenShortcuts, shortcuts) = results.shortcuts!!.partition { - hiddenKeys.contains( - it.key - ) - } - hiddenItems += hiddenShortcuts - appShortcutResults.value = shortcuts.applyRanking(query) - } else { - appShortcutResults.value = emptyList() - } - - if (results.files != null) { - val (hiddenFiles, files) = results.files!!.partition { - hiddenKeys.contains( - it.key - ) - } - hiddenItems += hiddenFiles - fileResults.value = files.applyRanking(query) - } else { - fileResults.value = emptyList() - } - - if (results.contacts != null) { - val (hiddenContacts, contacts) = results.contacts!!.partition { - hiddenKeys.contains( - it.key - ) - } - hiddenItems += hiddenContacts - contactResults.value = contacts.applyRanking(query) - } else { - contactResults.value = emptyList() - } - - if (results.calendars != null) { - val (hiddenEvents, events) = results.calendars!!.partition { - hiddenKeys.contains( - it.key - ) - } - hiddenItems += hiddenEvents - calendarResults.value = events.applyRanking(query) - } else { - calendarResults.value = emptyList() - } - - if (results.locations != null && results.locations!!.isNotEmpty()) { - val (hiddenLocations, locations) = results.locations!!.partition { - hiddenKeys.contains( - it.key - ) - } - hiddenItems += hiddenLocations - val lastLocation = devicePoseProvider.lastLocation - if (lastLocation != null) { - locationResults.value = locations.asSequence() - .sortedWith { a, b -> - a.distanceTo(lastLocation) - .compareTo(b.distanceTo(lastLocation)) - } - .distinctBy { it.key } - .toList() - } else { - locationResults.value = locations.applyRanking(query) - } - } else { - locationResults.value = emptyList() - } - - if (results.wikipedia != null) { - articleResults.value = results.wikipedia!!.applyRanking(query) - } else { - articleResults.value = emptyList() - } - - if (results.websites != null) { - websiteResults.value = results.websites!!.applyRanking(query) - } else { - websiteResults.value = emptyList() - } - - - calculatorResults.value = results.calculators ?: emptyList() - unitConverterResults.value = results.unitConverters ?: emptyList() + previousResults = results + + hiddenResults.clear() + workAppResults.clear() + privateSpaceAppResults.clear() + + mergeStateLists( + appResults, + results.apps?.filterNot { hiddenKeys.contains(it.key) } + ?.applyRanking(query) ?: emptyList() + ) + mergeStateLists( + appShortcutResults, + results.shortcuts?.filterNot { hiddenKeys.contains(it.key) } + ?.applyRanking(query) ?: emptyList() + ) + mergeStateLists( + fileResults, + results.files?.filterNot { hiddenKeys.contains(it.key) } + ?.applyRanking(query) ?: emptyList() + ) + mergeStateLists( + contactResults, + results.contacts?.filterNot { hiddenKeys.contains(it.key) } + ?.applyRanking(query) ?: emptyList() + ) + mergeStateLists( + calendarResults, + results.calendars?.filterNot { hiddenKeys.contains(it.key) } + ?.applyRanking(query) ?: emptyList() + ) + mergeStateLists( + locationResults, + results.locations?.filterNot { hiddenKeys.contains(it.key) } + ?.let { locations -> + devicePoseProvider.lastLocation?.let { + locations.asSequence() + .sortedWith { a, b -> + a.distanceTo(it).compareTo(b.distanceTo(it)) + } + .distinctBy { it.key } + .toList() + } ?: locations.applyRanking(query) + } ?: emptyList() + ) + mergeStateLists( + articleResults, + results.wikipedia?.applyRanking(query) ?: emptyList() + ) + mergeStateLists( + websiteResults, + results.websites?.applyRanking(query) ?: emptyList() + ) + mergeStateLists(calculatorResults, results.calculators ?: emptyList()) + mergeStateLists(unitConverterResults, results.unitConverters ?: emptyList()) if (results.searchActions != null) { - searchActionResults.value = results.searchActions!! + mergeStateLists(searchActionResults, results.searchActions!!) } if (launchOnEnter.value) { bestMatch.value = when { - appResults.value.isNotEmpty() -> appResults.value.first() - appShortcutResults.value.isNotEmpty() -> appShortcutResults.value.first() - calendarResults.value.isNotEmpty() -> calendarResults.value.first() - locationResults.value.isNotEmpty() -> locationResults.value.first() - contactResults.value.isNotEmpty() -> contactResults.value.first() - articleResults.value.isNotEmpty() -> articleResults.value.first() - websiteResults.value.isNotEmpty() -> websiteResults.value.first() - fileResults.value.isNotEmpty() -> fileResults.value.first() - searchActionResults.value.isNotEmpty() -> searchActionResults.value.first() + appResults.isNotEmpty() -> appResults.first() + appShortcutResults.isNotEmpty() -> appShortcutResults.first() + calendarResults.isNotEmpty() -> calendarResults.first() + locationResults.isNotEmpty() -> locationResults.first() + contactResults.isNotEmpty() -> contactResults.first() + articleResults.isNotEmpty() -> articleResults.first() + websiteResults.isNotEmpty() -> websiteResults.first() + fileResults.isNotEmpty() -> fileResults.first() + searchActionResults.isNotEmpty() -> searchActionResults.first() else -> null } } else { diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/searchbar/LauncherSearchBar.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/searchbar/LauncherSearchBar.kt index 57d6fe6b4..864728a06 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/searchbar/LauncherSearchBar.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/searchbar/LauncherSearchBar.kt @@ -68,7 +68,7 @@ fun LauncherSearchBar( val searchVM: SearchVM = viewModel() val hiddenItemsButtonEnabled by searchVM.hiddenResultsButton.collectAsState(false) - val hiddenItems by searchVM.hiddenResults + val hiddenItems = searchVM.hiddenResults val sheetManager = LocalBottomSheetManager.current diff --git a/core/base/src/main/java/de/mm20/launcher2/search/SearchableRepository.kt b/core/base/src/main/java/de/mm20/launcher2/search/SearchableRepository.kt index 65ffabbf8..cd113809a 100644 --- a/core/base/src/main/java/de/mm20/launcher2/search/SearchableRepository.kt +++ b/core/base/src/main/java/de/mm20/launcher2/search/SearchableRepository.kt @@ -1,6 +1,5 @@ package de.mm20.launcher2.search -import kotlinx.collections.immutable.ImmutableList import kotlinx.coroutines.flow.Flow interface SearchableRepository { diff --git a/services/search/src/main/java/de/mm20/launcher2/search/SearchService.kt b/services/search/src/main/java/de/mm20/launcher2/search/SearchService.kt index 533509828..a6d25e7eb 100644 --- a/services/search/src/main/java/de/mm20/launcher2/search/SearchService.kt +++ b/services/search/src/main/java/de/mm20/launcher2/search/SearchService.kt @@ -30,6 +30,7 @@ interface SearchService { fun search( query: String, filters: SearchFilters, + state: SearchResults? = null ): Flow fun getAllApps(): Flow @@ -54,9 +55,10 @@ internal class SearchServiceImpl( override fun search( query: String, filters: SearchFilters, + state: SearchResults? ): Flow = flow { supervisorScope { - val results = MutableStateFlow(SearchResults()) + val results = MutableStateFlow(state ?: SearchResults()) val customAttrResults = customAttributesRepository.search(query) .map { items -> From 98228a89723b8f8248eb7987e30416f6371e06f5 Mon Sep 17 00:00:00 2001 From: MM20 <15646950+MM2-0@users.noreply.github.com> Date: Sat, 26 Oct 2024 19:01:49 +0200 Subject: [PATCH 3/4] Rename parameter --- .../src/main/java/de/mm20/launcher2/search/SearchService.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/services/search/src/main/java/de/mm20/launcher2/search/SearchService.kt b/services/search/src/main/java/de/mm20/launcher2/search/SearchService.kt index a6d25e7eb..a55a43f67 100644 --- a/services/search/src/main/java/de/mm20/launcher2/search/SearchService.kt +++ b/services/search/src/main/java/de/mm20/launcher2/search/SearchService.kt @@ -30,7 +30,7 @@ interface SearchService { fun search( query: String, filters: SearchFilters, - state: SearchResults? = null + initialResults: SearchResults? = null, ): Flow fun getAllApps(): Flow @@ -55,10 +55,10 @@ internal class SearchServiceImpl( override fun search( query: String, filters: SearchFilters, - state: SearchResults? + initialResults: SearchResults?, ): Flow = flow { supervisorScope { - val results = MutableStateFlow(state ?: SearchResults()) + val results = MutableStateFlow(initialResults ?: SearchResults()) val customAttrResults = customAttributesRepository.search(query) .map { items -> From 76974db258644be8f0d04fb2cc42f6d48cf98e5e Mon Sep 17 00:00:00 2001 From: Christoph Liebender Date: Sat, 26 Oct 2024 22:49:46 +0200 Subject: [PATCH 4/4] mergeStateLists: refactor as extension method, add apps to SearchResults by default --- .../launcher2/ui/launcher/search/SearchVM.kt | 87 +++++++++---------- .../de/mm20/launcher2/search/Searchable.kt | 2 - 2 files changed, 39 insertions(+), 50 deletions(-) diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/SearchVM.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/SearchVM.kt index c9da67edd..54da56cfe 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/SearchVM.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/SearchVM.kt @@ -122,17 +122,6 @@ class SearchVM : ViewModel(), KoinComponent { var previousResults: SearchResults? = null - private fun mergeStateLists(state: SnapshotStateList, newItems: List) { - val diff = state.toSet() subtract newItems.toSet() - state.removeAll(diff) - for ((i, item) in newItems.withIndex()) { - if (i < state.size) - state[i] = item - else - state.add(item) - } - } - val hiddenResultsButton = searchUiSettings.hiddenItemsButton val hiddenResults = mutableStateListOf() @@ -258,12 +247,13 @@ class SearchVM : ViewModel(), KoinComponent { ) } hiddenItems += hiddenPrivateApps + previousResults = SearchResults(apps = apps) searchActionResults.clear() - mergeStateLists(appResults, apps) - mergeStateLists(workAppResults, workApps) - mergeStateLists(privateSpaceAppResults, privateApps) - mergeStateLists(hiddenResults, hiddenItems) + appResults.mergeWith(apps) + workAppResults.mergeWith(workApps) + privateSpaceAppResults.mergeWith(privateApps) + hiddenResults.mergeWith(hiddenItems) } } else { @@ -283,33 +273,19 @@ class SearchVM : ViewModel(), KoinComponent { workAppResults.clear() privateSpaceAppResults.clear() - mergeStateLists( - appResults, - results.apps?.filterNot { hiddenKeys.contains(it.key) } - ?.applyRanking(query) ?: emptyList() - ) - mergeStateLists( - appShortcutResults, - results.shortcuts?.filterNot { hiddenKeys.contains(it.key) } - ?.applyRanking(query) ?: emptyList() - ) - mergeStateLists( - fileResults, - results.files?.filterNot { hiddenKeys.contains(it.key) } - ?.applyRanking(query) ?: emptyList() - ) - mergeStateLists( - contactResults, + appResults.mergeWith(results.apps, hiddenKeys, query) + appShortcutResults.mergeWith(results.shortcuts, hiddenKeys, query) + fileResults.mergeWith(results.files, hiddenKeys, query) + + contactResults.mergeWith( results.contacts?.filterNot { hiddenKeys.contains(it.key) } - ?.applyRanking(query) ?: emptyList() + ?.applyRanking(query) ) - mergeStateLists( - calendarResults, + calendarResults.mergeWith( results.calendars?.filterNot { hiddenKeys.contains(it.key) } - ?.applyRanking(query) ?: emptyList() + ?.applyRanking(query) ) - mergeStateLists( - locationResults, + locationResults.mergeWith( results.locations?.filterNot { hiddenKeys.contains(it.key) } ?.let { locations -> devicePoseProvider.lastLocation?.let { @@ -320,21 +296,19 @@ class SearchVM : ViewModel(), KoinComponent { .distinctBy { it.key } .toList() } ?: locations.applyRanking(query) - } ?: emptyList() + } ) - mergeStateLists( - articleResults, - results.wikipedia?.applyRanking(query) ?: emptyList() + articleResults.mergeWith( + results.wikipedia?.applyRanking(query) ) - mergeStateLists( - websiteResults, - results.websites?.applyRanking(query) ?: emptyList() + websiteResults.mergeWith( + results.websites?.applyRanking(query) ) - mergeStateLists(calculatorResults, results.calculators ?: emptyList()) - mergeStateLists(unitConverterResults, results.unitConverters ?: emptyList()) + calculatorResults.mergeWith(results.calculators) + unitConverterResults.mergeWith(results.unitConverters) if (results.searchActions != null) { - mergeStateLists(searchActionResults, results.searchActions!!) + searchActionResults.mergeWith(results.searchActions!!) } if (launchOnEnter.value) { @@ -454,6 +428,23 @@ class SearchVM : ViewModel(), KoinComponent { } return sorted.distinctBy { it.key }.toList() } + + private fun SnapshotStateList.mergeWith(newItems: List?) { + val items = newItems ?: emptyList() + val diff = toSet() subtract items.toSet() + removeAll(diff) + for ((i, item) in items.withIndex()) { + if (i < size) + set(i, item) + else + add(item) + } + } + private suspend fun SnapshotStateList.mergeWith( + newItems: List?, + hiddenKeys: List, + query: String + ) = this.mergeWith((newItems ?: emptyList()).filterNot { hiddenKeys.contains(it.key) }.applyRanking(query)) } diff --git a/core/base/src/main/java/de/mm20/launcher2/search/Searchable.kt b/core/base/src/main/java/de/mm20/launcher2/search/Searchable.kt index d359192cf..38eae738f 100644 --- a/core/base/src/main/java/de/mm20/launcher2/search/Searchable.kt +++ b/core/base/src/main/java/de/mm20/launcher2/search/Searchable.kt @@ -1,7 +1,5 @@ package de.mm20.launcher2.search -import kotlinx.coroutines.Deferred - interface Searchable { val score: ResultScore get() = ResultScore.Unspecified