Use frequently used items settings in favorites
This commit is contained in:
parent
93f1f41d65
commit
c8bf1354b8
80
ui/src/main/java/de/mm20/launcher2/ui/common/FavoritesVM.kt
Normal file
80
ui/src/main/java/de/mm20/launcher2/ui/common/FavoritesVM.kt
Normal file
@ -0,0 +1,80 @@
|
||||
package de.mm20.launcher2.ui.common
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import de.mm20.launcher2.customattrs.CustomAttributesRepository
|
||||
import de.mm20.launcher2.favorites.FavoritesRepository
|
||||
import de.mm20.launcher2.preferences.LauncherDataStore
|
||||
import de.mm20.launcher2.search.data.Searchable
|
||||
import de.mm20.launcher2.widgets.WidgetRepository
|
||||
import kotlinx.coroutines.flow.*
|
||||
import org.koin.core.component.KoinComponent
|
||||
import org.koin.core.component.inject
|
||||
|
||||
open class FavoritesVM : ViewModel(), KoinComponent {
|
||||
|
||||
private val favoritesRepository: FavoritesRepository by inject()
|
||||
private val widgetRepository: WidgetRepository by inject()
|
||||
private val customAttributesRepository: CustomAttributesRepository by inject()
|
||||
private val dataStore: LauncherDataStore by inject()
|
||||
|
||||
val selectedTag = MutableStateFlow<String?>(null)
|
||||
|
||||
val favorites: Flow<List<Searchable>> = selectedTag.flatMapLatest { tag ->
|
||||
if (tag == null) {
|
||||
val columns = dataStore.data.map { it.grid.columnCount }
|
||||
val excludeCalendar = widgetRepository.isCalendarWidgetEnabled()
|
||||
val favoritesEnabled = dataStore.data.map { it.favorites.enabled }
|
||||
val includeFrequentlyUsed = dataStore.data.map { it.favorites.frequentlyUsed }
|
||||
val frequentlyUsedRows = dataStore.data.map { it.favorites.frequentlyUsedRows }
|
||||
|
||||
combine(
|
||||
listOf(
|
||||
favoritesEnabled,
|
||||
columns,
|
||||
excludeCalendar,
|
||||
includeFrequentlyUsed,
|
||||
frequentlyUsedRows
|
||||
)
|
||||
) { it }.transformLatest {
|
||||
|
||||
val favoritesEnabled = it[0] as Boolean
|
||||
val columns = it[1] as Int
|
||||
val excludeCalendar = it[2] as Boolean
|
||||
val includeFrequentlyUsed = it[3] as Boolean
|
||||
val frequentlyUsedRows = it[4] as Int
|
||||
|
||||
if (!favoritesEnabled) {
|
||||
return@transformLatest
|
||||
}
|
||||
|
||||
val pinned = favoritesRepository.getFavorites(
|
||||
excludeTypes = if (excludeCalendar) listOf("calendar") else null,
|
||||
manuallySorted = true,
|
||||
automaticallySorted = true,
|
||||
limit = 10 * columns,
|
||||
)
|
||||
if (includeFrequentlyUsed) {
|
||||
emitAll(pinned.flatMapLatest { pinned ->
|
||||
favoritesRepository.getFavorites(
|
||||
excludeTypes = if (excludeCalendar) listOf("calendar") else null,
|
||||
frequentlyUsed = true,
|
||||
limit = frequentlyUsedRows * columns - pinned.size % columns,
|
||||
).map {
|
||||
pinned + it
|
||||
}
|
||||
})
|
||||
} else {
|
||||
emitAll(pinned)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
emptyFlow<List<Searchable>>()
|
||||
}
|
||||
}.shareIn(viewModelScope, SharingStarted.WhileSubscribed(), replay = 1)
|
||||
|
||||
|
||||
fun selectTag(tag: String?) {
|
||||
selectedTag.value = tag
|
||||
}
|
||||
}
|
||||
@ -30,6 +30,7 @@ import de.mm20.launcher2.ui.launcher.modals.EditFavoritesSheet
|
||||
import de.mm20.launcher2.ui.launcher.search.calculator.CalculatorItem
|
||||
import de.mm20.launcher2.ui.launcher.search.common.grid.GridItem
|
||||
import de.mm20.launcher2.ui.launcher.search.common.list.ListItem
|
||||
import de.mm20.launcher2.ui.launcher.search.favorites.SearchFavoritesVM
|
||||
import de.mm20.launcher2.ui.launcher.search.hidden.HiddenResults
|
||||
import de.mm20.launcher2.ui.launcher.search.unitconverter.UnitConverterItem
|
||||
import de.mm20.launcher2.ui.launcher.search.website.WebsiteItem
|
||||
@ -51,12 +52,14 @@ fun SearchColumn(
|
||||
|
||||
val viewModel: SearchVM = viewModel()
|
||||
|
||||
val favoritesVM: SearchFavoritesVM = viewModel()
|
||||
val favorites by remember { favoritesVM.favorites }.collectAsState(emptyList())
|
||||
|
||||
val showLabels by viewModel.showLabels.observeAsState(true)
|
||||
|
||||
var showWorkProfileApps by remember { mutableStateOf(false) }
|
||||
|
||||
val hideFavs by viewModel.hideFavorites.observeAsState(true)
|
||||
val favorites by viewModel.favorites.observeAsState(emptyList())
|
||||
val apps by viewModel.appResults.observeAsState(emptyList())
|
||||
val workApps by viewModel.workAppResults.observeAsState(emptyList())
|
||||
val appShortcuts by viewModel.appShortcutResults.observeAsState(emptyList())
|
||||
|
||||
@ -18,6 +18,7 @@ import de.mm20.launcher2.permissions.PermissionsManager
|
||||
import de.mm20.launcher2.preferences.LauncherDataStore
|
||||
import de.mm20.launcher2.search.WebsearchRepository
|
||||
import de.mm20.launcher2.search.data.*
|
||||
import de.mm20.launcher2.ui.utils.withCustomLabels
|
||||
import de.mm20.launcher2.unitconverter.UnitConverterRepository
|
||||
import de.mm20.launcher2.websites.WebsiteRepository
|
||||
import de.mm20.launcher2.widgets.WidgetRepository
|
||||
@ -30,7 +31,6 @@ import org.koin.core.component.inject
|
||||
class SearchVM : ViewModel(), KoinComponent {
|
||||
|
||||
private val favoritesRepository: FavoritesRepository by inject()
|
||||
private val widgetRepository: WidgetRepository by inject()
|
||||
private val permissionsManager: PermissionsManager by inject()
|
||||
private val customAttributesRepository: CustomAttributesRepository by inject()
|
||||
private val dataStore: LauncherDataStore by inject()
|
||||
@ -52,8 +52,6 @@ class SearchVM : ViewModel(), KoinComponent {
|
||||
|
||||
val showLabels = dataStore.data.map { it.grid.showLabels }.asLiveData()
|
||||
|
||||
val favorites = MutableLiveData<List<Searchable>>(emptyList())
|
||||
|
||||
val appResults = MutableLiveData<List<Application>>(emptyList())
|
||||
val workAppResults = MutableLiveData<List<Application>>(emptyList())
|
||||
val appShortcutResults = MutableLiveData<List<AppShortcut>>(emptyList())
|
||||
@ -76,27 +74,6 @@ class SearchVM : ViewModel(), KoinComponent {
|
||||
|
||||
init {
|
||||
search("")
|
||||
viewModelScope.launch {
|
||||
dataStore.data.map { it.favorites.enabled }.collectLatest { enabled ->
|
||||
if (!enabled) {
|
||||
favorites.value = emptyList()
|
||||
return@collectLatest
|
||||
}
|
||||
widgetRepository.isCalendarWidgetEnabled().collectLatest { excludeCalendar ->
|
||||
dataStore.data.map { it.grid.columnCount }.collectLatest { columns ->
|
||||
favoritesRepository
|
||||
.getFavorites(
|
||||
columns = columns,
|
||||
excludeCalendarEvents = excludeCalendar
|
||||
)
|
||||
.withCustomLabels()
|
||||
.collectLatest {
|
||||
favorites.value = it
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var searchJob: Job? = null
|
||||
@ -131,7 +108,7 @@ class SearchVM : ViewModel(), KoinComponent {
|
||||
appRepository
|
||||
.search(query)
|
||||
.withCustomAttributeResults(customAttrResults)
|
||||
.withCustomLabels()
|
||||
.withCustomLabels(customAttributesRepository)
|
||||
.sorted()
|
||||
.collectWithHiddenItems(hiddenItemKeys) { results, hidden ->
|
||||
val (work, personal) = results.partition { it is LauncherApp && !it.isMainProfile }
|
||||
@ -146,7 +123,7 @@ class SearchVM : ViewModel(), KoinComponent {
|
||||
contactRepository
|
||||
.search(query)
|
||||
.withCustomAttributeResults(customAttrResults)
|
||||
.withCustomLabels()
|
||||
.withCustomLabels(customAttributesRepository)
|
||||
.sorted()
|
||||
.collectWithHiddenItems(hiddenItemKeys) { results, hidden ->
|
||||
contactResults.postValue(results)
|
||||
@ -159,7 +136,7 @@ class SearchVM : ViewModel(), KoinComponent {
|
||||
calendarRepository
|
||||
.search(query)
|
||||
.withCustomAttributeResults(customAttrResults)
|
||||
.withCustomLabels()
|
||||
.withCustomLabels(customAttributesRepository)
|
||||
.sorted()
|
||||
.collectWithHiddenItems(hiddenItemKeys) { results, hidden ->
|
||||
calendarResults.postValue(results)
|
||||
@ -192,7 +169,7 @@ class SearchVM : ViewModel(), KoinComponent {
|
||||
fileRepository
|
||||
.search(query)
|
||||
.withCustomAttributeResults(customAttrResults)
|
||||
.withCustomLabels()
|
||||
.withCustomLabels(customAttributesRepository)
|
||||
.sorted()
|
||||
.collectWithHiddenItems(hiddenItemKeys) { results, hidden ->
|
||||
fileResults.postValue(results)
|
||||
@ -210,7 +187,7 @@ class SearchVM : ViewModel(), KoinComponent {
|
||||
appShortcutRepository
|
||||
.search(query)
|
||||
.withCustomAttributeResults(customAttrResults)
|
||||
.withCustomLabels()
|
||||
.withCustomLabels(customAttributesRepository)
|
||||
.sorted()
|
||||
.collectWithHiddenItems(hiddenItemKeys) { results, hidden ->
|
||||
appShortcutResults.postValue(results)
|
||||
@ -306,21 +283,6 @@ class SearchVM : ViewModel(), KoinComponent {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inject custom labels and sort by the actual label
|
||||
*/
|
||||
private fun <T : Searchable> Flow<List<T>>.withCustomLabels(): Flow<List<T>> = channelFlow {
|
||||
this@withCustomLabels.collectLatest { items ->
|
||||
val labelsFlow = customAttributesRepository.getCustomLabels(items)
|
||||
labelsFlow.collectLatest { labels ->
|
||||
for (item in items) {
|
||||
val customLabel = labels.find { it.key == item.key }
|
||||
item.labelOverride = customLabel?.label
|
||||
}
|
||||
send(items)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private inline fun <reified T : Searchable> Flow<List<T>>.withCustomAttributeResults(
|
||||
customAttributeResults: Flow<List<Searchable>>
|
||||
|
||||
@ -1,33 +0,0 @@
|
||||
package de.mm20.launcher2.ui.launcher.search.favorites
|
||||
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.foundation.layout.ColumnScope
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.livedata.observeAsState
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import de.mm20.launcher2.ui.component.LauncherCard
|
||||
import de.mm20.launcher2.ui.launcher.search.SearchVM
|
||||
import de.mm20.launcher2.ui.launcher.search.common.grid.SearchResultGrid
|
||||
|
||||
@Composable
|
||||
fun FavoritesResults(
|
||||
reverse: Boolean = false,
|
||||
) {
|
||||
val viewModel: SearchVM = viewModel()
|
||||
val favorites by viewModel.favorites.observeAsState(emptyList())
|
||||
|
||||
val hide by viewModel.hideFavorites.observeAsState(true)
|
||||
|
||||
AnimatedVisibility(!hide && favorites.isNotEmpty()) {
|
||||
LauncherCard(
|
||||
modifier = Modifier
|
||||
.padding(bottom = if (reverse) 0.dp else 8.dp, top = if (reverse) 8.dp else 0.dp)
|
||||
) {
|
||||
SearchResultGrid(items = favorites, reverse = reverse)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,5 @@
|
||||
package de.mm20.launcher2.ui.launcher.search.favorites
|
||||
|
||||
import de.mm20.launcher2.ui.common.FavoritesVM
|
||||
|
||||
class SearchFavoritesVM: FavoritesVM()
|
||||
@ -1,15 +1,17 @@
|
||||
package de.mm20.launcher2.ui.launcher.widgets.favorites
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.livedata.observeAsState
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import de.mm20.launcher2.ui.launcher.search.common.grid.SearchResultGrid
|
||||
|
||||
@Composable
|
||||
fun FavoritesWidget() {
|
||||
val viewModel: FavoritesWidgetVM = viewModel()
|
||||
val favorites by viewModel.favorites.observeAsState(emptyList())
|
||||
val favorites by remember { viewModel.favorites }.collectAsState(emptyList())
|
||||
|
||||
SearchResultGrid(favorites)
|
||||
}
|
||||
@ -6,6 +6,7 @@ import androidx.lifecycle.viewModelScope
|
||||
import de.mm20.launcher2.favorites.FavoritesRepository
|
||||
import de.mm20.launcher2.preferences.LauncherDataStore
|
||||
import de.mm20.launcher2.search.data.Searchable
|
||||
import de.mm20.launcher2.ui.common.FavoritesVM
|
||||
import de.mm20.launcher2.widgets.WidgetRepository
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.map
|
||||
@ -13,24 +14,4 @@ import kotlinx.coroutines.launch
|
||||
import org.koin.core.component.KoinComponent
|
||||
import org.koin.core.component.inject
|
||||
|
||||
class FavoritesWidgetVM: ViewModel(), KoinComponent {
|
||||
private val favoritesRepository: FavoritesRepository by inject()
|
||||
private val widgetRepository: WidgetRepository by inject()
|
||||
private val dataStore: LauncherDataStore by inject()
|
||||
val favorites = MutableLiveData<List<Searchable>>(emptyList())
|
||||
|
||||
init {
|
||||
viewModelScope.launch {
|
||||
widgetRepository.isCalendarWidgetEnabled().collectLatest { excludeCalendar ->
|
||||
dataStore.data.map { it.grid.columnCount }.collectLatest { columns ->
|
||||
favoritesRepository.getFavorites(
|
||||
columns = columns,
|
||||
excludeCalendarEvents = excludeCalendar
|
||||
).collectLatest {
|
||||
favorites.value = it
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
class FavoritesWidgetVM: FavoritesVM()
|
||||
23
ui/src/main/java/de/mm20/launcher2/ui/utils/CustomLabels.kt
Normal file
23
ui/src/main/java/de/mm20/launcher2/ui/utils/CustomLabels.kt
Normal file
@ -0,0 +1,23 @@
|
||||
package de.mm20.launcher2.ui.utils
|
||||
|
||||
import de.mm20.launcher2.customattrs.CustomAttributesRepository
|
||||
import de.mm20.launcher2.customattrs.CustomLabel
|
||||
import de.mm20.launcher2.search.data.Searchable
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.channelFlow
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
|
||||
fun <T : Searchable> Flow<List<T>>.withCustomLabels(
|
||||
customAttributesRepository: CustomAttributesRepository,
|
||||
): Flow<List<T>> = channelFlow {
|
||||
this@withCustomLabels.collectLatest { items ->
|
||||
val customLabels = customAttributesRepository.getCustomLabels(items)
|
||||
customLabels.collectLatest { labels ->
|
||||
for (item in items) {
|
||||
val customLabel = labels.find { it.key == item.key }
|
||||
item.labelOverride = customLabel?.label
|
||||
}
|
||||
send(items)
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user