Custom labels
This commit is contained in:
@@ -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),
|
||||
|
||||
+15
-9
@@ -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
|
||||
|
||||
+11
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user