Custom labels

This commit is contained in:
MM20
2022-09-11 18:42:02 +02:00
parent 29a06dfc4f
commit 7b86677fc2
18 changed files with 161 additions and 63 deletions
@@ -10,6 +10,7 @@ import de.mm20.launcher2.appshortcuts.AppShortcutRepository
import de.mm20.launcher2.calculator.CalculatorRepository
import de.mm20.launcher2.calendar.CalendarRepository
import de.mm20.launcher2.contacts.ContactRepository
import de.mm20.launcher2.customattrs.CustomAttributesRepository
import de.mm20.launcher2.favorites.FavoritesRepository
import de.mm20.launcher2.files.FileRepository
import de.mm20.launcher2.permissions.PermissionGroup
@@ -31,6 +32,7 @@ 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()
private val calendarRepository: CalendarRepository by inject()
@@ -81,12 +83,15 @@ class SearchVM : ViewModel(), KoinComponent {
}
widgetRepository.isCalendarWidgetEnabled().collectLatest { excludeCalendar ->
dataStore.data.map { it.grid.columnCount }.collectLatest { columns ->
favoritesRepository.getFavorites(
columns = columns,
excludeCalendarEvents = excludeCalendar
).collectLatest {
favorites.value = it
}
favoritesRepository
.getFavorites(
columns = columns,
excludeCalendarEvents = excludeCalendar
)
.withCustomLabels()
.collectLatest {
favorites.value = it
}
}
}
}
@@ -103,44 +108,53 @@ class SearchVM : ViewModel(), KoinComponent {
try {
searchJob?.cancel()
} catch (e: CancellationException) {
} catch (_: CancellationException) {
}
hideFavorites.postValue(query.isNotEmpty())
searchJob = viewModelScope.launch {
isSearching.postValue(true)
val jobs = mutableListOf<Deferred<Any>>()
jobs += async(Dispatchers.Default) {
appRepository.search(query).collectLatest { apps ->
hiddenItemKeys.collectLatest { hiddenKeys ->
val results = apps.partition { !hiddenKeys.contains(it.key) }
appResults.postValue(results.first)
hiddenItems.update {
it.copy(apps = results.second)
appRepository
.search(query)
.withCustomLabels()
.collectLatest { apps ->
hiddenItemKeys.collectLatest { hiddenKeys ->
val results = apps.partition { !hiddenKeys.contains(it.key) }
appResults.postValue(results.first)
hiddenItems.update {
it.copy(apps = results.second)
}
}
}
}
}
jobs += async(Dispatchers.Default) {
contactRepository.search(query).collectLatest { contacts ->
hiddenItemKeys.collectLatest { hiddenKeys ->
val results = contacts.partition { !hiddenKeys.contains(it.key) }
contactResults.postValue(results.first)
hiddenItems.update {
it.copy(contacts = results.second)
contactRepository
.search(query)
.withCustomLabels()
.collectLatest { contacts ->
hiddenItemKeys.collectLatest { hiddenKeys ->
val results = contacts.partition { !hiddenKeys.contains(it.key) }
contactResults.postValue(results.first)
hiddenItems.update {
it.copy(contacts = results.second)
}
}
}
}
}
jobs += async(Dispatchers.Default) {
calendarRepository.search(query).collectLatest { events ->
hiddenItemKeys.collectLatest { hiddenKeys ->
val results = events.partition { !hiddenKeys.contains(it.key) }
calendarResults.postValue(results.first)
hiddenItems.update {
it.copy(calendarEvents = results.second)
calendarRepository
.search(query)
.withCustomLabels()
.collectLatest { events ->
hiddenItemKeys.collectLatest { hiddenKeys ->
val results = events.partition { !hiddenKeys.contains(it.key) }
calendarResults.postValue(results.first)
hiddenItems.update {
it.copy(calendarEvents = results.second)
}
}
}
}
}
jobs += async(Dispatchers.Default) {
wikipediaRepository.search(query).collectLatest {
@@ -163,15 +177,18 @@ class SearchVM : ViewModel(), KoinComponent {
}
}
jobs += async(Dispatchers.Default) {
fileRepository.search(query).collectLatest { files ->
hiddenItemKeys.collectLatest { hiddenKeys ->
val results = files.partition { !hiddenKeys.contains(it.key) }
fileResults.postValue(results.first)
hiddenItems.update {
it.copy(files = results.second)
fileRepository
.search(query)
.withCustomLabels()
.collectLatest { files ->
hiddenItemKeys.collectLatest { hiddenKeys ->
val results = files.partition { !hiddenKeys.contains(it.key) }
fileResults.postValue(results.first)
hiddenItems.update {
it.copy(files = results.second)
}
}
}
}
}
jobs += async(Dispatchers.Default) {
websearchRepository.search(query).collectLatest {
@@ -179,11 +196,14 @@ class SearchVM : ViewModel(), KoinComponent {
}
}
jobs += async(Dispatchers.Default) {
appShortcutRepository.search(query).collectLatest { shortcuts ->
hiddenItemKeys.collectLatest { hidden ->
appShortcutResults.postValue(shortcuts.filter { !hidden.contains(it.key) })
appShortcutRepository
.search(query)
.withCustomLabels()
.collectLatest { shortcuts ->
hiddenItemKeys.collectLatest { hidden ->
appShortcutResults.postValue(shortcuts.filter { !hidden.contains(it.key) })
}
}
}
}
launch(Dispatchers.Default) {
hiddenItems.collectLatest {
@@ -272,6 +292,22 @@ 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.sorted())
}
}
}
}
private data class HiddenItemResults(
@@ -62,7 +62,7 @@ fun AppItem(
.weight(1f)
.padding(16.dp)
) {
Text(text = app.label, style = MaterialTheme.typography.titleMedium)
Text(text = app.labelOverride ?: app.label, style = MaterialTheme.typography.titleMedium)
app.version?.let {
Text(
text = stringResource(R.string.app_info_version, it),
@@ -80,7 +80,7 @@ fun CalendarItem(
if (showDetails) MaterialTheme.typography.titleMedium
else MaterialTheme.typography.titleSmall
)
Text(text = calendar.label, style = textStyle)
Text(text = calendar.labelOverride ?: calendar.label, style = textStyle)
AnimatedVisibility(!showDetails) {
Text(
modifier = Modifier.padding(top = 2.dp),
@@ -97,23 +97,29 @@ fun CustomizeSearchableSheet(
viewModel.openIconPicker()
}
)
var customLabelValue by remember {
mutableStateOf(searchable.labelOverride ?: "")
}
OutlinedTextField(
modifier =
Modifier
.fillMaxWidth()
.padding(top = 24.dp)
.clickable {
Toast
.makeText(context, "Soon™", Toast.LENGTH_SHORT)
.show()
},
enabled = false,
value = searchable.label,
onValueChange = {},
.padding(top = 24.dp),
value = customLabelValue,
onValueChange = {
customLabelValue = it
},
placeholder = {
Text(searchable.label)
},
)
DisposableEffect(searchable.key) {
onDispose {
viewModel.setCustomLabel(customLabelValue)
}
}
}
} else {
val iconSize = 48.dp
@@ -2,7 +2,9 @@ package de.mm20.launcher2.ui.launcher.search.common.customattrs
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.liveData
import de.mm20.launcher2.customattrs.CustomAttributesRepository
import de.mm20.launcher2.customattrs.CustomIcon
import de.mm20.launcher2.customattrs.customAttrsModule
import de.mm20.launcher2.icons.CustomIconWithPreview
import de.mm20.launcher2.icons.IconRepository
import de.mm20.launcher2.icons.LauncherIcon
@@ -17,6 +19,7 @@ class CustomizeSearchableSheetVM(
private val searchable: Searchable
) : KoinComponent {
private val iconRepository: IconRepository by inject()
private val customAttributesRepository: CustomAttributesRepository by inject()
val isIconPickerOpen = MutableLiveData(false)
@@ -65,4 +68,12 @@ class CustomizeSearchableSheetVM(
}
}
}
fun setCustomLabel(label: String) {
if (label.isBlank()) {
customAttributesRepository.clearCustomLabel(searchable)
} else {
customAttributesRepository.setCustomLabel(searchable, label)
}
}
}
@@ -98,7 +98,7 @@ fun GridItem(modifier: Modifier = Modifier, item: Searchable, showLabels: Boolea
modifier = Modifier
.fillMaxWidth()
.padding(top = 8.dp),
text = item.label,
text = item.labelOverride ?: item.label,
textAlign = TextAlign.Center,
style = MaterialTheme.typography.bodySmall,
maxLines = 1,
@@ -80,7 +80,7 @@ fun ContactItem(
else MaterialTheme.typography.titleSmall
)
Text(
text = contact.label,
text = contact.labelOverride ?: contact.label,
style = textStyle,
maxLines = 1,
overflow = TextOverflow.Ellipsis
@@ -72,7 +72,7 @@ fun FileItem(
else MaterialTheme.typography.titleSmall
)
Text(
text = file.label,
text = file.labelOverride ?: file.label,
style = textStyle,
maxLines = 1,
overflow = TextOverflow.Ellipsis
@@ -65,7 +65,7 @@ fun AppShortcutItem(
) {
val titleStyle by animateTextStyleAsState(if (showDetails) MaterialTheme.typography.titleMedium else MaterialTheme.typography.titleSmall)
Text(
text = shortcut.label,
text = shortcut.labelOverride ?: shortcut.label,
style = titleStyle,
maxLines = 1,
overflow = TextOverflow.Ellipsis