Add filter bar customizations
This commit is contained in:
parent
5d824108c7
commit
94893a2c43
@ -301,7 +301,7 @@ fun Modifier.dragAndDrop(
|
|||||||
dragHorizontal: Boolean = true,
|
dragHorizontal: Boolean = true,
|
||||||
hapticFeedback: HapticFeedback
|
hapticFeedback: HapticFeedback
|
||||||
) =
|
) =
|
||||||
this then pointerInput(null) {
|
this then Modifier.pointerInput(null) {
|
||||||
val scope = CoroutineScope(coroutineContext)
|
val scope = CoroutineScope(coroutineContext)
|
||||||
detectDragGesturesAfterLongPress(
|
detectDragGesturesAfterLongPress(
|
||||||
onDragStart = { offset ->
|
onDragStart = { offset ->
|
||||||
|
|||||||
@ -96,6 +96,7 @@ class SearchVM : ViewModel(), KoinComponent {
|
|||||||
private val defaultFilters = searchFilterSettings.defaultFilter.stateIn(viewModelScope, SharingStarted.Eagerly, SearchFilters())
|
private val defaultFilters = searchFilterSettings.defaultFilter.stateIn(viewModelScope, SharingStarted.Eagerly, SearchFilters())
|
||||||
val filters = mutableStateOf(defaultFilters.value)
|
val filters = mutableStateOf(defaultFilters.value)
|
||||||
val filterBar = searchFilterSettings.filterBar
|
val filterBar = searchFilterSettings.filterBar
|
||||||
|
val filterBarItems = searchFilterSettings.filterBarItems
|
||||||
|
|
||||||
val separateWorkProfile = searchUiSettings.separateWorkProfile
|
val separateWorkProfile = searchUiSettings.separateWorkProfile
|
||||||
|
|
||||||
|
|||||||
@ -31,14 +31,21 @@ import androidx.compose.material3.VerticalDivider
|
|||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import de.mm20.launcher2.preferences.KeyboardFilterBarItem
|
||||||
import de.mm20.launcher2.search.SearchFilters
|
import de.mm20.launcher2.search.SearchFilters
|
||||||
import de.mm20.launcher2.ui.R
|
import de.mm20.launcher2.ui.R
|
||||||
import de.mm20.launcher2.ui.icons.Wikipedia
|
import de.mm20.launcher2.ui.icons.Wikipedia
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun KeyboardFilterBar(filters: SearchFilters, onFiltersChange: (SearchFilters) -> Unit) {
|
fun KeyboardFilterBar(
|
||||||
|
filters: SearchFilters,
|
||||||
|
onFiltersChange: (SearchFilters) -> Unit,
|
||||||
|
items: List<KeyboardFilterBarItem>,
|
||||||
|
) {
|
||||||
|
val context = LocalContext.current
|
||||||
val allCategoriesEnabled = filters.allCategoriesEnabled
|
val allCategoriesEnabled = filters.allCategoriesEnabled
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
@ -54,179 +61,33 @@ fun KeyboardFilterBar(filters: SearchFilters, onFiltersChange: (SearchFilters) -
|
|||||||
.padding(horizontal = 8.dp),
|
.padding(horizontal = 8.dp),
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
) {
|
) {
|
||||||
FilterChip(
|
for (i in items.indices) {
|
||||||
selected = filters.allowNetwork,
|
val item = items[i]
|
||||||
onClick = {
|
val prevItem = items.getOrNull(i - 1)
|
||||||
onFiltersChange(filters.copy(allowNetwork = !filters.allowNetwork))
|
if (prevItem != null && prevItem.isCategory != item.isCategory) {
|
||||||
},
|
|
||||||
leadingIcon = {
|
|
||||||
Icon(
|
|
||||||
imageVector = Icons.Rounded.Language,
|
|
||||||
contentDescription = null,
|
|
||||||
modifier = Modifier.size(FilterChipDefaults.IconSize)
|
|
||||||
)
|
|
||||||
},
|
|
||||||
label = { Text(stringResource(R.string.search_filter_online)) }
|
|
||||||
)
|
|
||||||
VerticalDivider(
|
VerticalDivider(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.height(36.dp)
|
.height(36.dp)
|
||||||
.padding(horizontal = 8.dp)
|
.padding(end = 8.dp)
|
||||||
)
|
)
|
||||||
|
}
|
||||||
FilterChip(
|
FilterChip(
|
||||||
modifier = Modifier.padding(end = 8.dp),
|
modifier = Modifier.padding(end = if (i == items.lastIndex) 0.dp else 8.dp),
|
||||||
selected = filters.apps && !allCategoriesEnabled,
|
selected = filters.isSelected(item),
|
||||||
onClick = {
|
onClick = {
|
||||||
onFiltersChange(filters.toggleApps())
|
onFiltersChange(filters.toggle(item))
|
||||||
},
|
},
|
||||||
leadingIcon = {
|
leadingIcon = {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.Rounded.Apps,
|
imageVector = item.icon,
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
modifier = Modifier.size(FilterChipDefaults.IconSize)
|
modifier = Modifier.size(FilterChipDefaults.IconSize)
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
label = { Text(stringResource(R.string.search_filter_apps)) }
|
label = { Text(item.getLabel(context)) }
|
||||||
)
|
|
||||||
FilterChip(
|
|
||||||
modifier = Modifier.padding(end = 8.dp),
|
|
||||||
selected = filters.files && !allCategoriesEnabled,
|
|
||||||
onClick = {
|
|
||||||
onFiltersChange(filters.toggleFiles())
|
|
||||||
},
|
|
||||||
leadingIcon = {
|
|
||||||
Icon(
|
|
||||||
imageVector = Icons.Rounded.Description,
|
|
||||||
contentDescription = null,
|
|
||||||
modifier = Modifier.size(FilterChipDefaults.IconSize)
|
|
||||||
)
|
|
||||||
},
|
|
||||||
label = { Text(stringResource(R.string.preference_search_files)) }
|
|
||||||
)
|
|
||||||
FilterChip(
|
|
||||||
modifier = Modifier.padding(end = 8.dp),
|
|
||||||
selected = filters.contacts && !allCategoriesEnabled,
|
|
||||||
onClick = {
|
|
||||||
onFiltersChange(filters.toggleContacts())
|
|
||||||
},
|
|
||||||
leadingIcon = {
|
|
||||||
Icon(
|
|
||||||
imageVector = Icons.Rounded.Person,
|
|
||||||
contentDescription = null,
|
|
||||||
modifier = Modifier.size(FilterChipDefaults.IconSize)
|
|
||||||
)
|
|
||||||
},
|
|
||||||
label = { Text(stringResource(R.string.preference_search_contacts)) }
|
|
||||||
)
|
|
||||||
FilterChip(
|
|
||||||
modifier = Modifier.padding(end = 8.dp),
|
|
||||||
selected = filters.events && !allCategoriesEnabled,
|
|
||||||
onClick = {
|
|
||||||
onFiltersChange(filters.toggleEvents())
|
|
||||||
},
|
|
||||||
leadingIcon = {
|
|
||||||
Icon(
|
|
||||||
imageVector = Icons.Rounded.Today,
|
|
||||||
contentDescription = null,
|
|
||||||
modifier = Modifier.size(FilterChipDefaults.IconSize)
|
|
||||||
)
|
|
||||||
},
|
|
||||||
label = { Text(stringResource(R.string.preference_search_calendar)) }
|
|
||||||
)
|
|
||||||
FilterChip(
|
|
||||||
modifier = Modifier.padding(end = 8.dp),
|
|
||||||
selected = filters.shortcuts && !allCategoriesEnabled,
|
|
||||||
onClick = {
|
|
||||||
onFiltersChange(filters.toggleShortcuts())
|
|
||||||
},
|
|
||||||
leadingIcon = {
|
|
||||||
Icon(
|
|
||||||
imageVector = Icons.Rounded.AppShortcut,
|
|
||||||
contentDescription = null,
|
|
||||||
modifier = Modifier.size(FilterChipDefaults.IconSize)
|
|
||||||
)
|
|
||||||
},
|
|
||||||
label = { Text(stringResource(R.string.preference_search_appshortcuts)) }
|
|
||||||
)
|
|
||||||
FilterChip(
|
|
||||||
modifier = Modifier.padding(end = 8.dp),
|
|
||||||
selected = filters.articles && !allCategoriesEnabled,
|
|
||||||
onClick = {
|
|
||||||
onFiltersChange(filters.toggleArticles())
|
|
||||||
},
|
|
||||||
leadingIcon = {
|
|
||||||
Icon(
|
|
||||||
imageVector = Icons.Rounded.Wikipedia,
|
|
||||||
contentDescription = null,
|
|
||||||
modifier = Modifier.size(FilterChipDefaults.IconSize)
|
|
||||||
)
|
|
||||||
},
|
|
||||||
label = { Text(stringResource(R.string.preference_search_wikipedia)) }
|
|
||||||
)
|
|
||||||
FilterChip(
|
|
||||||
modifier = Modifier.padding(end = 8.dp),
|
|
||||||
selected = filters.websites && !allCategoriesEnabled,
|
|
||||||
onClick = {
|
|
||||||
onFiltersChange(filters.toggleWebsites())
|
|
||||||
},
|
|
||||||
leadingIcon = {
|
|
||||||
Icon(
|
|
||||||
imageVector = Icons.Rounded.Public,
|
|
||||||
contentDescription = null,
|
|
||||||
modifier = Modifier.size(FilterChipDefaults.IconSize)
|
|
||||||
)
|
|
||||||
},
|
|
||||||
label = { Text(stringResource(R.string.preference_search_websites)) }
|
|
||||||
)
|
|
||||||
FilterChip(
|
|
||||||
modifier = Modifier.padding(end = 8.dp),
|
|
||||||
selected = filters.places && !allCategoriesEnabled,
|
|
||||||
onClick = {
|
|
||||||
onFiltersChange(filters.togglePlaces())
|
|
||||||
},
|
|
||||||
leadingIcon = {
|
|
||||||
Icon(
|
|
||||||
imageVector = Icons.Rounded.Place,
|
|
||||||
contentDescription = null,
|
|
||||||
modifier = Modifier.size(FilterChipDefaults.IconSize)
|
|
||||||
)
|
|
||||||
},
|
|
||||||
label = { Text(stringResource(R.string.preference_search_locations)) }
|
|
||||||
)
|
|
||||||
FilterChip(
|
|
||||||
selected = filters.tools && !allCategoriesEnabled,
|
|
||||||
onClick = {
|
|
||||||
onFiltersChange(filters.toggleTools())
|
|
||||||
},
|
|
||||||
leadingIcon = {
|
|
||||||
Icon(
|
|
||||||
imageVector = Icons.Rounded.Handyman,
|
|
||||||
contentDescription = null,
|
|
||||||
modifier = Modifier.size(FilterChipDefaults.IconSize)
|
|
||||||
)
|
|
||||||
},
|
|
||||||
label = { Text(stringResource(R.string.search_filter_tools)) }
|
|
||||||
)
|
|
||||||
VerticalDivider(
|
|
||||||
modifier = Modifier
|
|
||||||
.height(36.dp)
|
|
||||||
.padding(horizontal = 8.dp)
|
|
||||||
)
|
|
||||||
FilterChip(
|
|
||||||
selected = filters.hiddenItems,
|
|
||||||
onClick = {
|
|
||||||
onFiltersChange(filters.copy(hiddenItems = !filters.hiddenItems))
|
|
||||||
},
|
|
||||||
leadingIcon = {
|
|
||||||
Icon(
|
|
||||||
imageVector = Icons.Rounded.VisibilityOff,
|
|
||||||
contentDescription = null,
|
|
||||||
modifier = Modifier.size(FilterChipDefaults.IconSize)
|
|
||||||
)
|
|
||||||
},
|
|
||||||
label = { Text(stringResource(R.string.preference_hidden_items)) }
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
HorizontalDivider()
|
HorizontalDivider()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -0,0 +1,88 @@
|
|||||||
|
package de.mm20.launcher2.ui.launcher.search.filters
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.rounded.AppShortcut
|
||||||
|
import androidx.compose.material.icons.rounded.Apps
|
||||||
|
import androidx.compose.material.icons.rounded.Description
|
||||||
|
import androidx.compose.material.icons.rounded.Handyman
|
||||||
|
import androidx.compose.material.icons.rounded.Language
|
||||||
|
import androidx.compose.material.icons.rounded.Person
|
||||||
|
import androidx.compose.material.icons.rounded.Place
|
||||||
|
import androidx.compose.material.icons.rounded.Public
|
||||||
|
import androidx.compose.material.icons.rounded.Today
|
||||||
|
import androidx.compose.material.icons.rounded.VisibilityOff
|
||||||
|
import de.mm20.launcher2.preferences.KeyboardFilterBarItem
|
||||||
|
import de.mm20.launcher2.search.SearchFilters
|
||||||
|
import de.mm20.launcher2.ui.R
|
||||||
|
import de.mm20.launcher2.ui.icons.Wikipedia
|
||||||
|
|
||||||
|
val KeyboardFilterBarItem.icon
|
||||||
|
get() = when (this) {
|
||||||
|
KeyboardFilterBarItem.Apps -> Icons.Rounded.Apps
|
||||||
|
KeyboardFilterBarItem.Events -> Icons.Rounded.Today
|
||||||
|
KeyboardFilterBarItem.Contacts -> Icons.Rounded.Person
|
||||||
|
KeyboardFilterBarItem.Places -> Icons.Rounded.Place
|
||||||
|
KeyboardFilterBarItem.Files -> Icons.Rounded.Description
|
||||||
|
KeyboardFilterBarItem.Tools -> Icons.Rounded.Handyman
|
||||||
|
KeyboardFilterBarItem.Articles -> Icons.Rounded.Wikipedia
|
||||||
|
KeyboardFilterBarItem.Websites -> Icons.Rounded.Public
|
||||||
|
KeyboardFilterBarItem.Shortcuts -> Icons.Rounded.AppShortcut
|
||||||
|
KeyboardFilterBarItem.HiddenResults -> Icons.Rounded.VisibilityOff
|
||||||
|
KeyboardFilterBarItem.OnlineResults -> Icons.Rounded.Language
|
||||||
|
}
|
||||||
|
|
||||||
|
fun KeyboardFilterBarItem.getLabel(context: Context): String {
|
||||||
|
return when (this) {
|
||||||
|
KeyboardFilterBarItem.Apps -> context.getString(R.string.search_filter_apps)
|
||||||
|
KeyboardFilterBarItem.Events -> context.getString(R.string.preference_search_calendar)
|
||||||
|
KeyboardFilterBarItem.Contacts -> context.getString(R.string.preference_search_contacts)
|
||||||
|
KeyboardFilterBarItem.Places -> context.getString(R.string.preference_search_locations)
|
||||||
|
KeyboardFilterBarItem.Files -> context.getString(R.string.preference_search_files)
|
||||||
|
KeyboardFilterBarItem.Tools -> context.getString(R.string.search_filter_tools)
|
||||||
|
KeyboardFilterBarItem.Articles -> context.getString(R.string.preference_search_wikipedia)
|
||||||
|
KeyboardFilterBarItem.Websites -> context.getString(R.string.preference_search_websites)
|
||||||
|
KeyboardFilterBarItem.Shortcuts -> context.getString(R.string.preference_search_appshortcuts)
|
||||||
|
KeyboardFilterBarItem.HiddenResults -> context.getString(R.string.preference_hidden_items)
|
||||||
|
KeyboardFilterBarItem.OnlineResults -> context.getString(R.string.search_filter_online)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val KeyboardFilterBarItem.isCategory
|
||||||
|
get() = when (this) {
|
||||||
|
KeyboardFilterBarItem.OnlineResults, KeyboardFilterBarItem.HiddenResults -> false
|
||||||
|
else -> true
|
||||||
|
}
|
||||||
|
|
||||||
|
fun SearchFilters.isSelected(item: KeyboardFilterBarItem): Boolean {
|
||||||
|
if (item.isCategory && allCategoriesEnabled) return false
|
||||||
|
return when (item) {
|
||||||
|
KeyboardFilterBarItem.Apps -> apps
|
||||||
|
KeyboardFilterBarItem.Events -> events
|
||||||
|
KeyboardFilterBarItem.Contacts -> contacts
|
||||||
|
KeyboardFilterBarItem.Places -> places
|
||||||
|
KeyboardFilterBarItem.Files -> files
|
||||||
|
KeyboardFilterBarItem.Tools -> tools
|
||||||
|
KeyboardFilterBarItem.Articles -> articles
|
||||||
|
KeyboardFilterBarItem.Websites -> websites
|
||||||
|
KeyboardFilterBarItem.Shortcuts -> shortcuts
|
||||||
|
KeyboardFilterBarItem.HiddenResults -> hiddenItems
|
||||||
|
KeyboardFilterBarItem.OnlineResults -> allowNetwork
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun SearchFilters.toggle(item: KeyboardFilterBarItem): SearchFilters {
|
||||||
|
return when (item) {
|
||||||
|
KeyboardFilterBarItem.Apps -> return toggleApps()
|
||||||
|
KeyboardFilterBarItem.Events -> return toggleEvents()
|
||||||
|
KeyboardFilterBarItem.Contacts -> return toggleContacts()
|
||||||
|
KeyboardFilterBarItem.Places -> return togglePlaces()
|
||||||
|
KeyboardFilterBarItem.Files -> return toggleFiles()
|
||||||
|
KeyboardFilterBarItem.Tools -> return toggleTools()
|
||||||
|
KeyboardFilterBarItem.Articles -> return toggleArticles()
|
||||||
|
KeyboardFilterBarItem.Websites -> return toggleWebsites()
|
||||||
|
KeyboardFilterBarItem.Shortcuts -> return toggleShortcuts()
|
||||||
|
KeyboardFilterBarItem.HiddenResults -> return copy(hiddenItems = !hiddenItems)
|
||||||
|
KeyboardFilterBarItem.OnlineResults -> return copy(allowNetwork = !allowNetwork)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -137,11 +137,13 @@ fun LauncherSearchBar(
|
|||||||
exit = fadeOut(),
|
exit = fadeOut(),
|
||||||
modifier = Modifier.align(Alignment.BottomCenter)
|
modifier = Modifier.align(Alignment.BottomCenter)
|
||||||
) {
|
) {
|
||||||
|
val items by searchVM.filterBarItems.collectAsState(emptyList())
|
||||||
KeyboardFilterBar(
|
KeyboardFilterBar(
|
||||||
filters = searchVM.filters.value,
|
filters = searchVM.filters.value,
|
||||||
onFiltersChange = {
|
onFiltersChange = {
|
||||||
searchVM.setFilters(it)
|
searchVM.setFilters(it)
|
||||||
}
|
},
|
||||||
|
items = items
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -42,6 +42,7 @@ import de.mm20.launcher2.ui.settings.debug.DebugSettingsScreen
|
|||||||
import de.mm20.launcher2.ui.settings.easteregg.EasterEggSettingsScreen
|
import de.mm20.launcher2.ui.settings.easteregg.EasterEggSettingsScreen
|
||||||
import de.mm20.launcher2.ui.settings.favorites.FavoritesSettingsScreen
|
import de.mm20.launcher2.ui.settings.favorites.FavoritesSettingsScreen
|
||||||
import de.mm20.launcher2.ui.settings.filesearch.FileSearchSettingsScreen
|
import de.mm20.launcher2.ui.settings.filesearch.FileSearchSettingsScreen
|
||||||
|
import de.mm20.launcher2.ui.settings.filterbar.FilterBarSettingsScreen
|
||||||
import de.mm20.launcher2.ui.settings.gestures.GestureSettingsScreen
|
import de.mm20.launcher2.ui.settings.gestures.GestureSettingsScreen
|
||||||
import de.mm20.launcher2.ui.settings.hiddenitems.HiddenItemsSettingsScreen
|
import de.mm20.launcher2.ui.settings.hiddenitems.HiddenItemsSettingsScreen
|
||||||
import de.mm20.launcher2.ui.settings.homescreen.HomescreenSettingsScreen
|
import de.mm20.launcher2.ui.settings.homescreen.HomescreenSettingsScreen
|
||||||
@ -170,6 +171,9 @@ class SettingsActivity : BaseActivity() {
|
|||||||
composable("settings/search/tags") {
|
composable("settings/search/tags") {
|
||||||
TagsSettingsScreen()
|
TagsSettingsScreen()
|
||||||
}
|
}
|
||||||
|
composable("settings/search/filterbar") {
|
||||||
|
FilterBarSettingsScreen()
|
||||||
|
}
|
||||||
composable(ROUTE_WEATHER_INTEGRATION) {
|
composable(ROUTE_WEATHER_INTEGRATION) {
|
||||||
WeatherIntegrationSettingsScreen()
|
WeatherIntegrationSettingsScreen()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,139 @@
|
|||||||
|
package de.mm20.launcher2.ui.settings.filterbar
|
||||||
|
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.compose.animation.core.animateDpAsState
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.rounded.ArrowBack
|
||||||
|
import androidx.compose.material3.CenterAlignedTopAppBar
|
||||||
|
import androidx.compose.material3.HorizontalDivider
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.IconButton
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Scaffold
|
||||||
|
import androidx.compose.material3.Surface
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
|
import androidx.compose.runtime.derivedStateOf
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.zIndex
|
||||||
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
|
import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
||||||
|
import de.mm20.launcher2.preferences.KeyboardFilterBarItem
|
||||||
|
import de.mm20.launcher2.ui.R
|
||||||
|
import de.mm20.launcher2.ui.component.dragndrop.DraggableItem
|
||||||
|
import de.mm20.launcher2.ui.component.dragndrop.LazyDragAndDropColumn
|
||||||
|
import de.mm20.launcher2.ui.component.dragndrop.rememberLazyDragAndDropListState
|
||||||
|
import de.mm20.launcher2.ui.component.preferences.SwitchPreference
|
||||||
|
import de.mm20.launcher2.ui.launcher.search.filters.getLabel
|
||||||
|
import de.mm20.launcher2.ui.launcher.search.filters.icon
|
||||||
|
import de.mm20.launcher2.ui.launcher.search.filters.isCategory
|
||||||
|
import de.mm20.launcher2.ui.locals.LocalNavController
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun FilterBarSettingsScreen() {
|
||||||
|
val viewModel: FilterBarSettingsScreenVM = viewModel()
|
||||||
|
val navController = LocalNavController.current
|
||||||
|
val systemUiController = rememberSystemUiController()
|
||||||
|
systemUiController.setStatusBarColor(MaterialTheme.colorScheme.surface)
|
||||||
|
systemUiController.setNavigationBarColor(Color.Black)
|
||||||
|
|
||||||
|
val context = LocalContext.current
|
||||||
|
val activity = LocalContext.current as? AppCompatActivity
|
||||||
|
|
||||||
|
val listState = rememberLazyDragAndDropListState(
|
||||||
|
onDragStart = {
|
||||||
|
it.key is KeyboardFilterBarItem
|
||||||
|
},
|
||||||
|
onItemMove = { from, to ->
|
||||||
|
val item = (from.key as? KeyboardFilterBarItem) ?: return@rememberLazyDragAndDropListState
|
||||||
|
val toItem = (to.key as? KeyboardFilterBarItem) ?: return@rememberLazyDragAndDropListState
|
||||||
|
viewModel.moveItem(item, toItem)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
val enabledItems by viewModel.filterBarItems.collectAsState()
|
||||||
|
|
||||||
|
val disabledItems by remember {
|
||||||
|
derivedStateOf {
|
||||||
|
KeyboardFilterBarItem.entries.filter { enabledItems?.contains(it) == false }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Scaffold(
|
||||||
|
topBar = {
|
||||||
|
CenterAlignedTopAppBar(
|
||||||
|
title = {
|
||||||
|
Text(
|
||||||
|
stringResource(id = R.string.preference_customize_filter_bar),
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
modifier = Modifier.padding(horizontal = 16.dp),
|
||||||
|
maxLines = 1
|
||||||
|
)
|
||||||
|
},
|
||||||
|
navigationIcon = {
|
||||||
|
IconButton(onClick = {
|
||||||
|
if (navController?.navigateUp() != true) {
|
||||||
|
activity?.onBackPressed()
|
||||||
|
}
|
||||||
|
}) {
|
||||||
|
Icon(imageVector = Icons.Rounded.ArrowBack, contentDescription = "Back")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}) {
|
||||||
|
|
||||||
|
if (enabledItems == null) {
|
||||||
|
return@Scaffold
|
||||||
|
}
|
||||||
|
|
||||||
|
LazyDragAndDropColumn(
|
||||||
|
state = listState,
|
||||||
|
bidirectionalDrag = false,
|
||||||
|
contentPadding = it,
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
) {
|
||||||
|
for (i in 0 until KeyboardFilterBarItem.entries.size) {
|
||||||
|
val item = enabledItems!!.getOrNull(i) ?: disabledItems[i - enabledItems!!.size]
|
||||||
|
val prevItem = enabledItems!!.getOrNull(i - 1) ?: disabledItems.getOrNull(i - enabledItems!!.size - 1)
|
||||||
|
if (prevItem != null && prevItem.isCategory != item.isCategory) {
|
||||||
|
item(key = "divider-$i") {
|
||||||
|
HorizontalDivider()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
item(key = item) {
|
||||||
|
DraggableItem(state = listState, key = item) {
|
||||||
|
val elevation by animateDpAsState(if (it) 4.dp else 0.dp)
|
||||||
|
Surface(
|
||||||
|
shadowElevation = elevation,
|
||||||
|
tonalElevation = elevation,
|
||||||
|
modifier = Modifier.zIndex(if (it) 1f else 0f)
|
||||||
|
) {
|
||||||
|
SwitchPreference(
|
||||||
|
title = item.getLabel(context),
|
||||||
|
icon = item.icon,
|
||||||
|
value = enabledItems!!.contains(item), onValueChanged = {
|
||||||
|
if (it) {
|
||||||
|
viewModel.addAction(item)
|
||||||
|
} else {
|
||||||
|
viewModel.removeAction(item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,41 @@
|
|||||||
|
package de.mm20.launcher2.ui.settings.filterbar
|
||||||
|
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import de.mm20.launcher2.preferences.KeyboardFilterBarItem
|
||||||
|
import de.mm20.launcher2.preferences.search.SearchFilterSettings
|
||||||
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
|
import kotlinx.coroutines.flow.stateIn
|
||||||
|
import org.koin.core.component.KoinComponent
|
||||||
|
import org.koin.core.component.inject
|
||||||
|
|
||||||
|
class FilterBarSettingsScreenVM(
|
||||||
|
) : ViewModel(), KoinComponent {
|
||||||
|
private val searchFilterSettings: SearchFilterSettings by inject()
|
||||||
|
|
||||||
|
val filterBarItems = searchFilterSettings.filterBarItems
|
||||||
|
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
|
||||||
|
|
||||||
|
fun moveItem(item: KeyboardFilterBarItem, toItem: KeyboardFilterBarItem) {
|
||||||
|
val items = filterBarItems.value?.toMutableList() ?: return
|
||||||
|
val fromIndex = items.indexOf(item)
|
||||||
|
val toIndex = items.indexOf(toItem)
|
||||||
|
if (fromIndex > items.lastIndex) return
|
||||||
|
if (toIndex > items.lastIndex) return
|
||||||
|
if (fromIndex != -1) items.removeAt(fromIndex)
|
||||||
|
if (toIndex != -1) items.add(toIndex, item)
|
||||||
|
searchFilterSettings.setFilterBarItems(items)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addAction(item: KeyboardFilterBarItem) {
|
||||||
|
val items = filterBarItems.value?.toMutableList() ?: return
|
||||||
|
items.add(item)
|
||||||
|
searchFilterSettings.setFilterBarItems(items)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun removeAction(item: KeyboardFilterBarItem) {
|
||||||
|
val items = filterBarItems.value?.toMutableList() ?: return
|
||||||
|
items.remove(item)
|
||||||
|
searchFilterSettings.setFilterBarItems(items)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -290,6 +290,15 @@ fun SearchSettingsScreen() {
|
|||||||
viewModel.setFilterBar(it)
|
viewModel.setFilterBar(it)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
AnimatedVisibility(filterBar == true) {
|
||||||
|
Preference(
|
||||||
|
title = stringResource(R.string.preference_customize_filter_bar),
|
||||||
|
summary = stringResource(R.string.preference_customize_filter_bar_summary),
|
||||||
|
onClick = {
|
||||||
|
navController?.navigate("settings/search/filterbar")
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (hasWorkProfile) {
|
if (hasWorkProfile) {
|
||||||
|
|||||||
@ -922,5 +922,7 @@
|
|||||||
<string name="preference_default_filter_summary">Customize the default filter for searches</string>
|
<string name="preference_default_filter_summary">Customize the default filter for searches</string>
|
||||||
<string name="preference_filter_bar">Show filter bar</string>
|
<string name="preference_filter_bar">Show filter bar</string>
|
||||||
<string name="preference_filter_bar_summary">Show quick filters above the keyboard</string>
|
<string name="preference_filter_bar_summary">Show quick filters above the keyboard</string>
|
||||||
|
<string name="preference_customize_filter_bar">Customize filter bar</string>
|
||||||
|
<string name="preference_customize_filter_bar_summary">Customize which items are included in the filter bar</string>
|
||||||
<string name="filter_settings_network_warning">The current filter enables online results by default. Your search queries might unintentionally be sent to external web services. For privacy reasons, this is not recommended.</string>
|
<string name="filter_settings_network_warning">The current filter enables online results by default. Your search queries might unintentionally be sent to external web services. For privacy reasons, this is not recommended.</string>
|
||||||
</resources>
|
</resources>
|
||||||
@ -154,7 +154,6 @@ data class LauncherSettingsData internal constructor(
|
|||||||
KeyboardFilterBarItem.Events,
|
KeyboardFilterBarItem.Events,
|
||||||
KeyboardFilterBarItem.Contacts,
|
KeyboardFilterBarItem.Contacts,
|
||||||
KeyboardFilterBarItem.Files,
|
KeyboardFilterBarItem.Files,
|
||||||
KeyboardFilterBarItem.Places,
|
|
||||||
KeyboardFilterBarItem.Articles,
|
KeyboardFilterBarItem.Articles,
|
||||||
KeyboardFilterBarItem.Websites,
|
KeyboardFilterBarItem.Websites,
|
||||||
KeyboardFilterBarItem.Places,
|
KeyboardFilterBarItem.Places,
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
package de.mm20.launcher2.preferences.search
|
package de.mm20.launcher2.preferences.search
|
||||||
|
|
||||||
|
import de.mm20.launcher2.preferences.KeyboardFilterBarItem
|
||||||
import de.mm20.launcher2.preferences.LauncherDataStore
|
import de.mm20.launcher2.preferences.LauncherDataStore
|
||||||
import de.mm20.launcher2.search.SearchFilters
|
import de.mm20.launcher2.search.SearchFilters
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
@ -25,4 +26,12 @@ class SearchFilterSettings internal constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val filterBarItems
|
||||||
|
get() = launcherDataStore.data.map { it.searchFilterBarItems.distinct() }
|
||||||
|
|
||||||
|
fun setFilterBarItems(items: List<KeyboardFilterBarItem>) {
|
||||||
|
launcherDataStore.update {
|
||||||
|
it.copy(searchFilterBarItems = items)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user