Add preferences to hide filter bar and to configure the default filter
This commit is contained in:
parent
15f58352d9
commit
069b21a118
@ -59,8 +59,10 @@ fun AssistantScaffold(
|
||||
|
||||
val searchState = rememberLazyListState()
|
||||
|
||||
val filterBar by searchVM.filterBar.collectAsState(false)
|
||||
|
||||
val keyboardFilterBarPadding by animateDpAsState(
|
||||
if (WindowInsets.imeAnimationTarget.getBottom(LocalDensity.current) > 0 && !searchVM.showFilters.value) 50.dp else 0.dp
|
||||
if (WindowInsets.imeAnimationTarget.getBottom(LocalDensity.current) > 0 && !searchVM.showFilters.value && filterBar) 50.dp else 0.dp
|
||||
)
|
||||
|
||||
val isSearchAtStart by remember {
|
||||
|
||||
@ -129,8 +129,10 @@ fun PagerScaffold(
|
||||
|
||||
val pagerState = rememberPagerState { 2 }
|
||||
|
||||
val filterBar by searchVM.filterBar.collectAsState(false)
|
||||
|
||||
val keyboardFilterBarPadding by animateDpAsState(
|
||||
if (WindowInsets.imeAnimationTarget.getBottom(LocalDensity.current) > 0 && !searchVM.showFilters.value) 50.dp else 0.dp
|
||||
if (WindowInsets.imeAnimationTarget.getBottom(LocalDensity.current) > 0 && !searchVM.showFilters.value && filterBar) 50.dp else 0.dp
|
||||
)
|
||||
|
||||
val isSearchAtBottom by remember {
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
package de.mm20.launcher2.ui.launcher
|
||||
|
||||
import android.util.Log
|
||||
import android.view.HapticFeedbackConstants
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
@ -114,8 +115,9 @@ fun PullDownScaffold(
|
||||
val maxOffset = with(density) { 64.dp.toPx() }
|
||||
val toggleSearchThreshold = with(density) { 48.dp.toPx() }
|
||||
|
||||
val filterBar by searchVM.filterBar.collectAsState(false)
|
||||
val keyboardFilterBarPadding by animateDpAsState(
|
||||
if (WindowInsets.imeAnimationTarget.getBottom(LocalDensity.current) > 0 && !searchVM.showFilters.value) 50.dp else 0.dp
|
||||
if (WindowInsets.imeAnimationTarget.getBottom(LocalDensity.current) > 0 && !searchVM.showFilters.value && filterBar) 50.dp else 0.dp
|
||||
)
|
||||
|
||||
val isSearchAtTop by remember {
|
||||
|
||||
@ -93,8 +93,9 @@ class SearchVM : ViewModel(), KoinComponent {
|
||||
|
||||
val showFilters = mutableStateOf(false)
|
||||
|
||||
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 filterBar = searchFilterSettings.filterBar
|
||||
|
||||
val separateWorkProfile = searchUiSettings.separateWorkProfile
|
||||
|
||||
|
||||
@ -34,7 +34,8 @@ import de.mm20.launcher2.ui.icons.Wikipedia
|
||||
fun SearchFilters(
|
||||
filters: SearchFilters,
|
||||
onFiltersChange: (SearchFilters) -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
modifier: Modifier = Modifier,
|
||||
settings: Boolean = false
|
||||
) {
|
||||
val allCategoriesEnabled = filters.allCategoriesEnabled
|
||||
Column(
|
||||
@ -60,9 +61,13 @@ fun SearchFilters(
|
||||
FlowRow {
|
||||
FilterChip(
|
||||
modifier = Modifier.padding(end = 16.dp),
|
||||
selected = filters.apps && !allCategoriesEnabled,
|
||||
selected = filters.apps && (!allCategoriesEnabled || settings),
|
||||
onClick = {
|
||||
onFiltersChange(filters.toggleApps())
|
||||
if (settings) {
|
||||
onFiltersChange(filters.copy(apps = !filters.apps))
|
||||
} else {
|
||||
onFiltersChange(filters.toggleApps())
|
||||
}
|
||||
},
|
||||
leadingIcon = {
|
||||
Icon(
|
||||
@ -75,9 +80,13 @@ fun SearchFilters(
|
||||
)
|
||||
FilterChip(
|
||||
modifier = Modifier.padding(end = 16.dp),
|
||||
selected = filters.files && !allCategoriesEnabled,
|
||||
selected = filters.files && (!allCategoriesEnabled || settings),
|
||||
onClick = {
|
||||
onFiltersChange(filters.toggleFiles())
|
||||
if (settings) {
|
||||
onFiltersChange(filters.copy(files = !filters.files))
|
||||
} else {
|
||||
onFiltersChange(filters.toggleFiles())
|
||||
}
|
||||
},
|
||||
leadingIcon = {
|
||||
Icon(
|
||||
@ -90,9 +99,13 @@ fun SearchFilters(
|
||||
)
|
||||
FilterChip(
|
||||
modifier = Modifier.padding(end = 16.dp),
|
||||
selected = filters.contacts && !allCategoriesEnabled,
|
||||
selected = filters.contacts && (!allCategoriesEnabled || settings),
|
||||
onClick = {
|
||||
onFiltersChange(filters.toggleContacts())
|
||||
if (settings) {
|
||||
onFiltersChange(filters.copy(contacts = !filters.contacts))
|
||||
} else {
|
||||
onFiltersChange(filters.toggleContacts())
|
||||
}
|
||||
},
|
||||
leadingIcon = {
|
||||
Icon(
|
||||
@ -105,9 +118,13 @@ fun SearchFilters(
|
||||
)
|
||||
FilterChip(
|
||||
modifier = Modifier.padding(end = 16.dp),
|
||||
selected = filters.events && !allCategoriesEnabled,
|
||||
selected = filters.events && (!allCategoriesEnabled || settings),
|
||||
onClick = {
|
||||
onFiltersChange(filters.toggleEvents())
|
||||
if (settings) {
|
||||
onFiltersChange(filters.copy(events = !filters.events))
|
||||
} else {
|
||||
onFiltersChange(filters.toggleEvents())
|
||||
}
|
||||
},
|
||||
leadingIcon = {
|
||||
Icon(
|
||||
@ -120,9 +137,13 @@ fun SearchFilters(
|
||||
)
|
||||
FilterChip(
|
||||
modifier = Modifier.padding(end = 16.dp),
|
||||
selected = filters.shortcuts && !allCategoriesEnabled,
|
||||
selected = filters.shortcuts && (!allCategoriesEnabled || settings),
|
||||
onClick = {
|
||||
onFiltersChange(filters.toggleShortcuts())
|
||||
if (settings) {
|
||||
onFiltersChange(filters.copy(shortcuts = !filters.shortcuts))
|
||||
} else {
|
||||
onFiltersChange(filters.toggleShortcuts())
|
||||
}
|
||||
},
|
||||
leadingIcon = {
|
||||
Icon(
|
||||
@ -135,9 +156,13 @@ fun SearchFilters(
|
||||
)
|
||||
FilterChip(
|
||||
modifier = Modifier.padding(end = 16.dp),
|
||||
selected = filters.articles && !allCategoriesEnabled,
|
||||
selected = filters.articles && (!allCategoriesEnabled || settings),
|
||||
onClick = {
|
||||
onFiltersChange(filters.toggleArticles())
|
||||
if (settings) {
|
||||
onFiltersChange(filters.copy(articles = !filters.articles))
|
||||
} else {
|
||||
onFiltersChange(filters.toggleArticles())
|
||||
}
|
||||
},
|
||||
leadingIcon = {
|
||||
Icon(
|
||||
@ -150,9 +175,13 @@ fun SearchFilters(
|
||||
)
|
||||
FilterChip(
|
||||
modifier = Modifier.padding(end = 16.dp),
|
||||
selected = filters.websites && !allCategoriesEnabled,
|
||||
selected = filters.websites && (!allCategoriesEnabled || settings),
|
||||
onClick = {
|
||||
onFiltersChange(filters.toggleWebsites())
|
||||
if (settings) {
|
||||
onFiltersChange(filters.copy(websites = !filters.websites))
|
||||
} else {
|
||||
onFiltersChange(filters.toggleWebsites())
|
||||
}
|
||||
},
|
||||
leadingIcon = {
|
||||
Icon(
|
||||
@ -165,9 +194,13 @@ fun SearchFilters(
|
||||
)
|
||||
FilterChip(
|
||||
modifier = Modifier.padding(end = 16.dp),
|
||||
selected = filters.places && !allCategoriesEnabled,
|
||||
selected = filters.places && (!allCategoriesEnabled || settings),
|
||||
onClick = {
|
||||
onFiltersChange(filters.togglePlaces())
|
||||
if (settings) {
|
||||
onFiltersChange(filters.copy(places = !filters.places))
|
||||
} else {
|
||||
onFiltersChange(filters.togglePlaces())
|
||||
}
|
||||
},
|
||||
leadingIcon = {
|
||||
Icon(
|
||||
@ -179,9 +212,13 @@ fun SearchFilters(
|
||||
label = { Text(stringResource(R.string.preference_search_locations)) }
|
||||
)
|
||||
FilterChip(
|
||||
selected = filters.tools && !allCategoriesEnabled,
|
||||
selected = filters.tools && (!allCategoriesEnabled || settings),
|
||||
onClick = {
|
||||
onFiltersChange(filters.toggleTools())
|
||||
if (settings) {
|
||||
onFiltersChange(filters.copy(tools = !filters.tools))
|
||||
} else {
|
||||
onFiltersChange(filters.toggleTools())
|
||||
}
|
||||
},
|
||||
leadingIcon = {
|
||||
Icon(
|
||||
|
||||
@ -23,6 +23,8 @@ import androidx.compose.material3.IconButtonDefaults
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
@ -65,6 +67,7 @@ fun LauncherSearchBar(
|
||||
else focusManager.clearFocus()
|
||||
}
|
||||
|
||||
val filterBar by searchVM.filterBar.collectAsState(false)
|
||||
|
||||
val _value = value()
|
||||
|
||||
@ -127,7 +130,7 @@ fun LauncherSearchBar(
|
||||
onKeyboardActionGo = onKeyboardActionGo
|
||||
)
|
||||
|
||||
AnimatedVisibility (isSearchOpen && !searchVM.showFilters.value
|
||||
AnimatedVisibility (filterBar && isSearchOpen && !searchVM.showFilters.value
|
||||
// Use imeAnimationTarget instead of isImeVisible to animate the filter bar at the same time as the keyboard
|
||||
&& WindowInsets.imeAnimationTarget.getBottom(LocalDensity.current) > 0,
|
||||
enter = fadeIn(),
|
||||
|
||||
@ -1,37 +1,58 @@
|
||||
package de.mm20.launcher2.ui.settings.search
|
||||
|
||||
import android.content.Context
|
||||
import android.content.pm.LauncherApps
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.rounded.*
|
||||
import androidx.compose.material.icons.rounded.AppShortcut
|
||||
import androidx.compose.material.icons.rounded.ArrowOutward
|
||||
import androidx.compose.material.icons.rounded.Calculate
|
||||
import androidx.compose.material.icons.rounded.Description
|
||||
import androidx.compose.material.icons.rounded.FilterAlt
|
||||
import androidx.compose.material.icons.rounded.Keyboard
|
||||
import androidx.compose.material.icons.rounded.Loop
|
||||
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.Sort
|
||||
import androidx.compose.material.icons.rounded.Star
|
||||
import androidx.compose.material.icons.rounded.Tag
|
||||
import androidx.compose.material.icons.rounded.Today
|
||||
import androidx.compose.material.icons.rounded.VisibilityOff
|
||||
import androidx.compose.material.icons.rounded.Warning
|
||||
import androidx.compose.material.icons.rounded.Work
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalLifecycleOwner
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.content.getSystemService
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.compose.LocalLifecycleOwner
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.lifecycle.repeatOnLifecycle
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import de.mm20.launcher2.preferences.LegacySettings
|
||||
import de.mm20.launcher2.preferences.SearchResultOrder
|
||||
import de.mm20.launcher2.ui.R
|
||||
import de.mm20.launcher2.ui.component.BottomSheetDialog
|
||||
import de.mm20.launcher2.ui.component.MissingPermissionBanner
|
||||
import de.mm20.launcher2.ui.component.preferences.*
|
||||
import de.mm20.launcher2.ui.component.SmallMessage
|
||||
import de.mm20.launcher2.ui.component.preferences.ListPreference
|
||||
import de.mm20.launcher2.ui.component.preferences.Preference
|
||||
import de.mm20.launcher2.ui.component.preferences.PreferenceCategory
|
||||
import de.mm20.launcher2.ui.component.preferences.PreferenceScreen
|
||||
import de.mm20.launcher2.ui.component.preferences.PreferenceWithSwitch
|
||||
import de.mm20.launcher2.ui.component.preferences.SwitchPreference
|
||||
import de.mm20.launcher2.ui.icons.Wikipedia
|
||||
import de.mm20.launcher2.ui.launcher.search.filters.SearchFilters
|
||||
import de.mm20.launcher2.ui.locals.LocalNavController
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.flow.map
|
||||
|
||||
@Composable
|
||||
fun SearchSettingsScreen() {
|
||||
@ -50,6 +71,10 @@ fun SearchSettingsScreen() {
|
||||
}
|
||||
}
|
||||
|
||||
var showFilterEditor by remember {
|
||||
mutableStateOf(false)
|
||||
}
|
||||
|
||||
|
||||
PreferenceScreen(title = stringResource(R.string.preference_screen_search)) {
|
||||
item {
|
||||
@ -246,6 +271,27 @@ fun SearchSettingsScreen() {
|
||||
)
|
||||
}
|
||||
}
|
||||
item {
|
||||
val filterBar by viewModel.filterBar.collectAsStateWithLifecycle(null)
|
||||
PreferenceCategory {
|
||||
Preference(
|
||||
title = stringResource(R.string.preference_default_filter),
|
||||
summary = stringResource(R.string.preference_default_filter_summary),
|
||||
icon = Icons.Rounded.FilterAlt,
|
||||
onClick = {
|
||||
showFilterEditor = true
|
||||
},
|
||||
)
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.preference_filter_bar),
|
||||
summary = stringResource(R.string.preference_filter_bar_summary),
|
||||
value = filterBar == true,
|
||||
onValueChanged = {
|
||||
viewModel.setFilterBar(it)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
if (hasWorkProfile) {
|
||||
item {
|
||||
PreferenceCategory {
|
||||
@ -322,4 +368,28 @@ fun SearchSettingsScreen() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (showFilterEditor) {
|
||||
val filters by viewModel.searchFilters.collectAsStateWithLifecycle()
|
||||
BottomSheetDialog(onDismissRequest = { showFilterEditor = false }) {
|
||||
Column(
|
||||
modifier = Modifier.padding(it)
|
||||
) {
|
||||
AnimatedVisibility(filters.allowNetwork) {
|
||||
SmallMessage(
|
||||
modifier = Modifier.padding(bottom = 16.dp),
|
||||
icon = Icons.Rounded.Warning,
|
||||
text = stringResource(R.string.filter_settings_network_warning)
|
||||
)
|
||||
}
|
||||
SearchFilters(
|
||||
filters = filters,
|
||||
onFiltersChange = {
|
||||
viewModel.setSearchFilters(it)
|
||||
},
|
||||
settings = true,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -14,11 +14,13 @@ import de.mm20.launcher2.preferences.search.CalculatorSearchSettings
|
||||
import de.mm20.launcher2.preferences.search.CalendarSearchSettings
|
||||
import de.mm20.launcher2.preferences.search.ContactSearchSettings
|
||||
import de.mm20.launcher2.preferences.search.LocationSearchSettings
|
||||
import de.mm20.launcher2.preferences.search.SearchFilterSettings
|
||||
import de.mm20.launcher2.preferences.search.ShortcutSearchSettings
|
||||
import de.mm20.launcher2.preferences.search.UnitConverterSettings
|
||||
import de.mm20.launcher2.preferences.search.WebsiteSearchSettings
|
||||
import de.mm20.launcher2.preferences.search.WikipediaSearchSettings
|
||||
import de.mm20.launcher2.preferences.ui.SearchUiSettings
|
||||
import de.mm20.launcher2.search.SearchFilters
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
@ -34,6 +36,7 @@ class SearchSettingsScreenVM : ViewModel(), KoinComponent {
|
||||
private val websiteSearchSettings: WebsiteSearchSettings by inject()
|
||||
private val unitConverterSettings: UnitConverterSettings by inject()
|
||||
private val calculatorSearchSettings: CalculatorSearchSettings by inject()
|
||||
private val searchFilterSettings: SearchFilterSettings by inject()
|
||||
|
||||
private val permissionsManager: PermissionsManager by inject()
|
||||
private val locationSearchSettings: LocationSearchSettings by inject()
|
||||
@ -160,4 +163,18 @@ class SearchSettingsScreenVM : ViewModel(), KoinComponent {
|
||||
fun requestAppShortcutsPermission(activity: AppCompatActivity) {
|
||||
permissionsManager.requestPermission(activity, PermissionGroup.AppShortcuts)
|
||||
}
|
||||
|
||||
val filterBar = searchFilterSettings.filterBar
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
|
||||
|
||||
fun setFilterBar(filterBar: Boolean) {
|
||||
searchFilterSettings.setFilterBar(filterBar)
|
||||
}
|
||||
|
||||
val searchFilters = searchFilterSettings.defaultFilter
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), SearchFilters())
|
||||
|
||||
fun setSearchFilters(searchFilters: SearchFilters) {
|
||||
searchFilterSettings.setDefaultFilter(searchFilters)
|
||||
}
|
||||
}
|
||||
@ -918,4 +918,9 @@
|
||||
<string name="search_filter_tools">Tools</string>
|
||||
<string name="search_filter_online">Online results</string>
|
||||
<string name="search_filter_apps">Apps</string>
|
||||
<string name="preference_default_filter">Default filter</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_summary">Show quick filters above the keyboard</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>
|
||||
@ -1,6 +1,7 @@
|
||||
package de.mm20.launcher2.preferences.search
|
||||
|
||||
import de.mm20.launcher2.preferences.LauncherDataStore
|
||||
import de.mm20.launcher2.search.SearchFilters
|
||||
import kotlinx.coroutines.flow.map
|
||||
|
||||
class SearchFilterSettings internal constructor(
|
||||
@ -8,4 +9,20 @@ class SearchFilterSettings internal constructor(
|
||||
) {
|
||||
val defaultFilter
|
||||
get() = launcherDataStore.data.map { it.searchFilter }
|
||||
|
||||
fun setDefaultFilter(filter: SearchFilters) {
|
||||
launcherDataStore.update {
|
||||
it.copy(searchFilter = filter)
|
||||
}
|
||||
}
|
||||
|
||||
val filterBar
|
||||
get() = launcherDataStore.data.map { it.searchFilterBar }
|
||||
|
||||
fun setFilterBar(filterBar: Boolean) {
|
||||
launcherDataStore.update {
|
||||
it.copy(searchFilterBar = filterBar)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user