Add extended filter view
This commit is contained in:
parent
0ebf0f872d
commit
9b29696757
@ -250,7 +250,7 @@ fun PagerScaffold(
|
|||||||
} else {
|
} else {
|
||||||
pagerState.animateScrollToPage(0)
|
pagerState.animateScrollToPage(0)
|
||||||
}
|
}
|
||||||
searchVM.search("")
|
searchVM.reset()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -262,7 +262,7 @@ fun PagerScaffold(
|
|||||||
when {
|
when {
|
||||||
isSearchOpen -> {
|
isSearchOpen -> {
|
||||||
viewModel.closeSearch()
|
viewModel.closeSearch()
|
||||||
searchVM.search("")
|
searchVM.reset()
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -252,7 +252,7 @@ fun PullDownScaffold(
|
|||||||
animationSpec = spring(stiffness = Spring.StiffnessMediumLow)
|
animationSpec = spring(stiffness = Spring.StiffnessMediumLow)
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
searchVM.search("")
|
searchVM.reset()
|
||||||
if (viewModel.skipNextSearchAnimation) {
|
if (viewModel.skipNextSearchAnimation) {
|
||||||
pagerState.scrollToPage(0)
|
pagerState.scrollToPage(0)
|
||||||
viewModel.skipNextSearchAnimation = false
|
viewModel.skipNextSearchAnimation = false
|
||||||
@ -273,7 +273,7 @@ fun PullDownScaffold(
|
|||||||
when {
|
when {
|
||||||
isSearchOpen -> {
|
isSearchOpen -> {
|
||||||
viewModel.closeSearch()
|
viewModel.closeSearch()
|
||||||
searchVM.search("")
|
searchVM.reset()
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -279,7 +279,7 @@ abstract class SharedLauncherActivity(
|
|||||||
super.onResume()
|
super.onResume()
|
||||||
if (System.currentTimeMillis() - pauseTime > 20000) {
|
if (System.currentTimeMillis() - pauseTime > 20000) {
|
||||||
viewModel.closeSearchWithoutAnimation()
|
viewModel.closeSearchWithoutAnimation()
|
||||||
searchVM.search("")
|
searchVM.reset()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,10 +1,13 @@
|
|||||||
package de.mm20.launcher2.ui.launcher.search
|
package de.mm20.launcher2.ui.launcher.search
|
||||||
|
|
||||||
|
import androidx.activity.compose.BackHandler
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.compose.animation.AnimatedContent
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
@ -28,10 +31,10 @@ import androidx.compose.material3.Text
|
|||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateMapOf
|
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clipToBounds
|
import androidx.compose.ui.draw.clipToBounds
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
@ -39,7 +42,6 @@ import androidx.compose.ui.res.stringResource
|
|||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import de.mm20.launcher2.search.Location
|
|
||||||
import de.mm20.launcher2.search.SavableSearchable
|
import de.mm20.launcher2.search.SavableSearchable
|
||||||
import de.mm20.launcher2.ui.R
|
import de.mm20.launcher2.ui.R
|
||||||
import de.mm20.launcher2.ui.common.FavoritesTagSelector
|
import de.mm20.launcher2.ui.common.FavoritesTagSelector
|
||||||
@ -51,6 +53,7 @@ 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.grid.GridItem
|
||||||
import de.mm20.launcher2.ui.launcher.search.common.list.ListItem
|
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.favorites.SearchFavoritesVM
|
||||||
|
import de.mm20.launcher2.ui.launcher.search.filters.SearchFilters
|
||||||
import de.mm20.launcher2.ui.launcher.search.unitconverter.UnitConverterItem
|
import de.mm20.launcher2.ui.launcher.search.unitconverter.UnitConverterItem
|
||||||
import de.mm20.launcher2.ui.launcher.search.website.WebsiteItem
|
import de.mm20.launcher2.ui.launcher.search.website.WebsiteItem
|
||||||
import de.mm20.launcher2.ui.launcher.search.wikipedia.ArticleItem
|
import de.mm20.launcher2.ui.launcher.search.wikipedia.ArticleItem
|
||||||
@ -116,253 +119,290 @@ fun SearchColumn(
|
|||||||
val favoritesEditButton by favoritesVM.showEditButton.collectAsState(false)
|
val favoritesEditButton by favoritesVM.showEditButton.collectAsState(false)
|
||||||
val favoritesTagsExpanded by favoritesVM.tagsExpanded.collectAsState(false)
|
val favoritesTagsExpanded by favoritesVM.tagsExpanded.collectAsState(false)
|
||||||
|
|
||||||
LazyColumn(
|
val showFilters by viewModel.showFilters
|
||||||
state = state,
|
|
||||||
modifier = modifier,
|
|
||||||
userScrollEnabled = userScrollEnabled,
|
|
||||||
contentPadding = paddingValues,
|
|
||||||
reverseLayout = reverse,
|
|
||||||
) {
|
|
||||||
if (!hideFavs && favoritesEnabled) {
|
|
||||||
GridResults(
|
|
||||||
items = favorites.toImmutableList(),
|
|
||||||
columns = columns,
|
|
||||||
key = "favorites",
|
|
||||||
reverse = reverse,
|
|
||||||
before = if (favorites.isEmpty()) {
|
|
||||||
{
|
|
||||||
Banner(
|
|
||||||
modifier = Modifier.padding(16.dp),
|
|
||||||
text = stringResource(
|
|
||||||
if (selectedTag == null) R.string.favorites_empty else R.string.favorites_empty_tag
|
|
||||||
),
|
|
||||||
icon = if (selectedTag == null) Icons.Rounded.Star else Icons.Rounded.Tag,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else null,
|
|
||||||
after = if (pinnedTags.isEmpty() && !favoritesEditButton) {
|
|
||||||
null
|
|
||||||
} else {
|
|
||||||
{
|
|
||||||
FavoritesTagSelector(
|
|
||||||
tags = pinnedTags,
|
|
||||||
selectedTag = selectedTag,
|
|
||||||
editButton = favoritesEditButton,
|
|
||||||
reverse = reverse,
|
|
||||||
onSelectTag = { favoritesVM.selectTag(it) },
|
|
||||||
scrollState = tagsScrollState,
|
|
||||||
expanded = favoritesTagsExpanded,
|
|
||||||
onExpand = { favoritesVM.setTagsExpanded(it) }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
highlightedItem = bestMatch as? SavableSearchable
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
GridResults(
|
AnimatedContent(showFilters) {
|
||||||
items = if (separateWorkProfile) if ((showWorkProfileApps || apps.isEmpty()) && workApps.isNotEmpty()) workApps.toImmutableList() else apps.toImmutableList() else listOf(apps, workApps).flatten().sorted().toImmutableList(),
|
if (it) {
|
||||||
columns = columns,
|
BackHandler {
|
||||||
reverse = reverse,
|
viewModel.showFilters.value = false
|
||||||
key = "apps",
|
}
|
||||||
before = if (separateWorkProfile && workApps.isNotEmpty() && apps.isNotEmpty()) {
|
Box(
|
||||||
{
|
modifier = modifier
|
||||||
Row(
|
.fillMaxSize()
|
||||||
|
.padding(paddingValues),
|
||||||
|
contentAlignment = if (reverse) Alignment.BottomCenter else Alignment.TopCenter,
|
||||||
|
) {
|
||||||
|
LauncherCard(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(horizontal = 8.dp, vertical = 4.dp)
|
||||||
|
) {
|
||||||
|
SearchFilters(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(horizontal = 8.dp)
|
.padding(12.dp),
|
||||||
.padding(
|
filters = viewModel.filters.value,
|
||||||
top = if (reverse) 4.dp else 8.dp,
|
onFiltersChange = {
|
||||||
bottom = if (reverse) 8.dp else 4.dp
|
viewModel.setFilters(it)
|
||||||
),
|
}
|
||||||
) {
|
)
|
||||||
FilterChip(
|
}
|
||||||
modifier = Modifier.padding(horizontal = 8.dp),
|
}
|
||||||
selected = !showWorkProfileApps,
|
} else {
|
||||||
onClick = { showWorkProfileApps = false },
|
LazyColumn(
|
||||||
leadingIcon = {
|
state = state,
|
||||||
Icon(
|
modifier = modifier,
|
||||||
imageVector = Icons.Rounded.Person,
|
userScrollEnabled = userScrollEnabled,
|
||||||
contentDescription = null,
|
contentPadding = paddingValues,
|
||||||
modifier = Modifier.size(FilterChipDefaults.IconSize)
|
reverseLayout = reverse,
|
||||||
)
|
) {
|
||||||
},
|
if (!hideFavs && favoritesEnabled) {
|
||||||
label = {
|
GridResults(
|
||||||
Text(
|
items = favorites.toImmutableList(),
|
||||||
stringResource(R.string.apps_profile_main),
|
columns = columns,
|
||||||
maxLines = 1,
|
key = "favorites",
|
||||||
overflow = TextOverflow.Ellipsis
|
reverse = reverse,
|
||||||
|
before = if (favorites.isEmpty()) {
|
||||||
|
{
|
||||||
|
Banner(
|
||||||
|
modifier = Modifier.padding(16.dp),
|
||||||
|
text = stringResource(
|
||||||
|
if (selectedTag == null) R.string.favorites_empty else R.string.favorites_empty_tag
|
||||||
|
),
|
||||||
|
icon = if (selectedTag == null) Icons.Rounded.Star else Icons.Rounded.Tag,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
} else null,
|
||||||
FilterChip(
|
after = if (pinnedTags.isEmpty() && !favoritesEditButton) {
|
||||||
selected = showWorkProfileApps,
|
null
|
||||||
onClick = { showWorkProfileApps = true },
|
} else {
|
||||||
leadingIcon = {
|
{
|
||||||
Icon(
|
FavoritesTagSelector(
|
||||||
imageVector = Icons.Rounded.Work,
|
tags = pinnedTags,
|
||||||
contentDescription = null,
|
selectedTag = selectedTag,
|
||||||
modifier = Modifier.size(FilterChipDefaults.IconSize)
|
editButton = favoritesEditButton,
|
||||||
)
|
reverse = reverse,
|
||||||
},
|
onSelectTag = { favoritesVM.selectTag(it) },
|
||||||
label = {
|
scrollState = tagsScrollState,
|
||||||
Text(
|
expanded = favoritesTagsExpanded,
|
||||||
stringResource(R.string.apps_profile_work),
|
onExpand = { favoritesVM.setTagsExpanded(it) }
|
||||||
maxLines = 1,
|
|
||||||
overflow = TextOverflow.Ellipsis
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
},
|
||||||
|
highlightedItem = bestMatch as? SavableSearchable
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
GridResults(
|
||||||
|
items = if (separateWorkProfile) if ((showWorkProfileApps || apps.isEmpty()) && workApps.isNotEmpty()) workApps.toImmutableList() else apps.toImmutableList() else listOf(
|
||||||
|
apps,
|
||||||
|
workApps
|
||||||
|
).flatten().sorted().toImmutableList(),
|
||||||
|
columns = columns,
|
||||||
|
reverse = reverse,
|
||||||
|
key = "apps",
|
||||||
|
before = if (separateWorkProfile && workApps.isNotEmpty() && apps.isNotEmpty()) {
|
||||||
|
{
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(horizontal = 8.dp)
|
||||||
|
.padding(
|
||||||
|
top = if (reverse) 4.dp else 8.dp,
|
||||||
|
bottom = if (reverse) 8.dp else 4.dp
|
||||||
|
),
|
||||||
|
) {
|
||||||
|
FilterChip(
|
||||||
|
modifier = Modifier.padding(horizontal = 8.dp),
|
||||||
|
selected = !showWorkProfileApps,
|
||||||
|
onClick = { showWorkProfileApps = false },
|
||||||
|
leadingIcon = {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Rounded.Person,
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = Modifier.size(FilterChipDefaults.IconSize)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
label = {
|
||||||
|
Text(
|
||||||
|
stringResource(R.string.apps_profile_main),
|
||||||
|
maxLines = 1,
|
||||||
|
overflow = TextOverflow.Ellipsis
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
FilterChip(
|
||||||
|
selected = showWorkProfileApps,
|
||||||
|
onClick = { showWorkProfileApps = true },
|
||||||
|
leadingIcon = {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Rounded.Work,
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = Modifier.size(FilterChipDefaults.IconSize)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
label = {
|
||||||
|
Text(
|
||||||
|
stringResource(R.string.apps_profile_work),
|
||||||
|
maxLines = 1,
|
||||||
|
overflow = TextOverflow.Ellipsis
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else null,
|
||||||
|
highlightedItem = bestMatch as? SavableSearchable
|
||||||
|
)
|
||||||
|
ListResults(
|
||||||
|
before = if (missingShortcutsPermission && !isSearchEmpty) {
|
||||||
|
{
|
||||||
|
MissingPermissionBanner(
|
||||||
|
modifier = Modifier.padding(8.dp),
|
||||||
|
text = stringResource(
|
||||||
|
R.string.missing_permission_appshortcuts_search,
|
||||||
|
stringResource(R.string.app_name)
|
||||||
|
),
|
||||||
|
onClick = { viewModel.requestAppShortcutPermission(context as AppCompatActivity) },
|
||||||
|
secondaryAction = {
|
||||||
|
OutlinedButton(onClick = {
|
||||||
|
viewModel.disableAppShortcutSearch()
|
||||||
|
}) {
|
||||||
|
Text(
|
||||||
|
stringResource(R.string.turn_off),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else null,
|
||||||
|
items = appShortcuts.toImmutableList(),
|
||||||
|
reverse = reverse,
|
||||||
|
key = "shortcuts",
|
||||||
|
highlightedItem = bestMatch as? SavableSearchable
|
||||||
|
)
|
||||||
|
for (conv in unitConverter) {
|
||||||
|
SingleResult {
|
||||||
|
UnitConverterItem(unitConverter = conv)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else null,
|
for (calc in calculator) {
|
||||||
highlightedItem = bestMatch as? SavableSearchable
|
SingleResult {
|
||||||
)
|
CalculatorItem(calculator = calc)
|
||||||
ListResults(
|
}
|
||||||
before = if (missingShortcutsPermission && !isSearchEmpty) {
|
|
||||||
{
|
|
||||||
MissingPermissionBanner(
|
|
||||||
modifier = Modifier.padding(8.dp),
|
|
||||||
text = stringResource(
|
|
||||||
R.string.missing_permission_appshortcuts_search,
|
|
||||||
stringResource(R.string.app_name)
|
|
||||||
),
|
|
||||||
onClick = { viewModel.requestAppShortcutPermission(context as AppCompatActivity) },
|
|
||||||
secondaryAction = {
|
|
||||||
OutlinedButton(onClick = {
|
|
||||||
viewModel.disableAppShortcutSearch()
|
|
||||||
}) {
|
|
||||||
Text(
|
|
||||||
stringResource(R.string.turn_off),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
} else null,
|
ListResults(
|
||||||
items = appShortcuts.toImmutableList(),
|
before = if (missingCalendarPermission && !isSearchEmpty) {
|
||||||
reverse = reverse,
|
{
|
||||||
key = "shortcuts",
|
MissingPermissionBanner(
|
||||||
highlightedItem = bestMatch as? SavableSearchable
|
modifier = Modifier.padding(8.dp),
|
||||||
)
|
text = stringResource(R.string.missing_permission_calendar_search),
|
||||||
for (conv in unitConverter) {
|
onClick = { viewModel.requestCalendarPermission(context as AppCompatActivity) },
|
||||||
SingleResult {
|
secondaryAction = {
|
||||||
UnitConverterItem(unitConverter = conv)
|
OutlinedButton(onClick = {
|
||||||
|
viewModel.disableCalendarSearch()
|
||||||
|
}) {
|
||||||
|
Text(
|
||||||
|
stringResource(R.string.turn_off),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else null,
|
||||||
|
items = events.toImmutableList(),
|
||||||
|
reverse = reverse,
|
||||||
|
key = "events",
|
||||||
|
highlightedItem = bestMatch as? SavableSearchable
|
||||||
|
)
|
||||||
|
ListResults(
|
||||||
|
before = if (missingContactsPermission && !isSearchEmpty) {
|
||||||
|
{
|
||||||
|
MissingPermissionBanner(
|
||||||
|
modifier = Modifier.padding(8.dp),
|
||||||
|
text = stringResource(R.string.missing_permission_contact_search),
|
||||||
|
onClick = { viewModel.requestContactsPermission(context as AppCompatActivity) },
|
||||||
|
secondaryAction = {
|
||||||
|
OutlinedButton(onClick = {
|
||||||
|
viewModel.disableContactsSearch()
|
||||||
|
}) {
|
||||||
|
Text(
|
||||||
|
stringResource(R.string.turn_off),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else null,
|
||||||
|
items = contacts.toImmutableList(),
|
||||||
|
reverse = reverse,
|
||||||
|
key = "contacts",
|
||||||
|
highlightedItem = bestMatch as? SavableSearchable
|
||||||
|
)
|
||||||
|
ListResults(
|
||||||
|
before = if (missingLocationPermission && !isSearchEmpty) {
|
||||||
|
{
|
||||||
|
MissingPermissionBanner(
|
||||||
|
modifier = Modifier.padding(8.dp),
|
||||||
|
text = stringResource(R.string.missing_permission_location_search),
|
||||||
|
onClick = { viewModel.requestLocationPermission(context as AppCompatActivity) },
|
||||||
|
secondaryAction = {
|
||||||
|
OutlinedButton(onClick = {
|
||||||
|
viewModel.disableLocationSearch()
|
||||||
|
}) {
|
||||||
|
Text(
|
||||||
|
stringResource(R.string.turn_off),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else null,
|
||||||
|
items = locations.toImmutableList(),
|
||||||
|
reverse = reverse,
|
||||||
|
key = "locations",
|
||||||
|
highlightedItem = bestMatch as? SavableSearchable
|
||||||
|
)
|
||||||
|
for (wiki in wikipedia) {
|
||||||
|
SingleResult(highlight = bestMatch == wiki) {
|
||||||
|
ArticleItem(article = wiki)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (ws in website) {
|
||||||
|
SingleResult(highlight = bestMatch == ws) {
|
||||||
|
WebsiteItem(website = ws)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ListResults(
|
||||||
|
before = if (missingFilesPermission && !isSearchEmpty) {
|
||||||
|
{
|
||||||
|
MissingPermissionBanner(
|
||||||
|
modifier = Modifier.padding(8.dp),
|
||||||
|
text = stringResource(R.string.missing_permission_files_search),
|
||||||
|
onClick = { viewModel.requestFilesPermission(context as AppCompatActivity) },
|
||||||
|
secondaryAction = {
|
||||||
|
OutlinedButton(onClick = {
|
||||||
|
viewModel.disableFilesSearch()
|
||||||
|
}) {
|
||||||
|
Text(
|
||||||
|
stringResource(R.string.turn_off),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else null,
|
||||||
|
items = files.toImmutableList(),
|
||||||
|
reverse = reverse,
|
||||||
|
key = "files",
|
||||||
|
highlightedItem = bestMatch as? SavableSearchable
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (calc in calculator) {
|
|
||||||
SingleResult {
|
|
||||||
CalculatorItem(calculator = calc)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ListResults(
|
|
||||||
before = if (missingCalendarPermission && !isSearchEmpty) {
|
|
||||||
{
|
|
||||||
MissingPermissionBanner(
|
|
||||||
modifier = Modifier.padding(8.dp),
|
|
||||||
text = stringResource(R.string.missing_permission_calendar_search),
|
|
||||||
onClick = { viewModel.requestCalendarPermission(context as AppCompatActivity) },
|
|
||||||
secondaryAction = {
|
|
||||||
OutlinedButton(onClick = {
|
|
||||||
viewModel.disableCalendarSearch()
|
|
||||||
}) {
|
|
||||||
Text(
|
|
||||||
stringResource(R.string.turn_off),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else null,
|
|
||||||
items = events.toImmutableList(),
|
|
||||||
reverse = reverse,
|
|
||||||
key = "events",
|
|
||||||
highlightedItem = bestMatch as? SavableSearchable
|
|
||||||
)
|
|
||||||
ListResults(
|
|
||||||
before = if (missingContactsPermission && !isSearchEmpty) {
|
|
||||||
{
|
|
||||||
MissingPermissionBanner(
|
|
||||||
modifier = Modifier.padding(8.dp),
|
|
||||||
text = stringResource(R.string.missing_permission_contact_search),
|
|
||||||
onClick = { viewModel.requestContactsPermission(context as AppCompatActivity) },
|
|
||||||
secondaryAction = {
|
|
||||||
OutlinedButton(onClick = {
|
|
||||||
viewModel.disableContactsSearch()
|
|
||||||
}) {
|
|
||||||
Text(
|
|
||||||
stringResource(R.string.turn_off),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else null,
|
|
||||||
items = contacts.toImmutableList(),
|
|
||||||
reverse = reverse,
|
|
||||||
key = "contacts",
|
|
||||||
highlightedItem = bestMatch as? SavableSearchable
|
|
||||||
)
|
|
||||||
ListResults(
|
|
||||||
before = if (missingLocationPermission && !isSearchEmpty) {
|
|
||||||
{
|
|
||||||
MissingPermissionBanner(
|
|
||||||
modifier = Modifier.padding(8.dp),
|
|
||||||
text = stringResource(R.string.missing_permission_location_search),
|
|
||||||
onClick = { viewModel.requestLocationPermission(context as AppCompatActivity) },
|
|
||||||
secondaryAction = {
|
|
||||||
OutlinedButton(onClick = {
|
|
||||||
viewModel.disableLocationSearch()
|
|
||||||
}) {
|
|
||||||
Text(
|
|
||||||
stringResource(R.string.turn_off),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else null,
|
|
||||||
items = locations.toImmutableList(),
|
|
||||||
reverse = reverse,
|
|
||||||
key = "locations",
|
|
||||||
highlightedItem = bestMatch as? SavableSearchable
|
|
||||||
)
|
|
||||||
for (wiki in wikipedia) {
|
|
||||||
SingleResult(highlight = bestMatch == wiki) {
|
|
||||||
ArticleItem(article = wiki)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (ws in website) {
|
|
||||||
SingleResult(highlight = bestMatch == ws) {
|
|
||||||
WebsiteItem(website = ws)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ListResults(
|
|
||||||
before = if (missingFilesPermission && !isSearchEmpty) {
|
|
||||||
{
|
|
||||||
MissingPermissionBanner(
|
|
||||||
modifier = Modifier.padding(8.dp),
|
|
||||||
text = stringResource(R.string.missing_permission_files_search),
|
|
||||||
onClick = { viewModel.requestFilesPermission(context as AppCompatActivity) },
|
|
||||||
secondaryAction = {
|
|
||||||
OutlinedButton(onClick = {
|
|
||||||
viewModel.disableFilesSearch()
|
|
||||||
}) {
|
|
||||||
Text(
|
|
||||||
stringResource(R.string.turn_off),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else null,
|
|
||||||
items = files.toImmutableList(),
|
|
||||||
reverse = reverse,
|
|
||||||
key = "files",
|
|
||||||
highlightedItem = bestMatch as? SavableSearchable
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
val sheetManager = LocalBottomSheetManager.current
|
val sheetManager = LocalBottomSheetManager.current
|
||||||
if (sheetManager.hiddenItemsSheetShown.value) {
|
if (sheetManager.hiddenItemsSheetShown.value) {
|
||||||
HiddenItemsSheet(
|
HiddenItemsSheet(
|
||||||
|
|||||||
@ -91,6 +91,8 @@ class SearchVM : ViewModel(), KoinComponent {
|
|||||||
val favoritesEnabled = searchUiSettings.favorites
|
val favoritesEnabled = searchUiSettings.favorites
|
||||||
val hideFavorites = mutableStateOf(false)
|
val hideFavorites = mutableStateOf(false)
|
||||||
|
|
||||||
|
val showFilters = mutableStateOf(false)
|
||||||
|
|
||||||
val filters = mutableStateOf(SearchFilters())
|
val filters = mutableStateOf(SearchFilters())
|
||||||
|
|
||||||
val separateWorkProfile = searchUiSettings.separateWorkProfile
|
val separateWorkProfile = searchUiSettings.separateWorkProfile
|
||||||
@ -125,9 +127,21 @@ class SearchVM : ViewModel(), KoinComponent {
|
|||||||
search(searchQuery.value, forceRestart = true)
|
search(searchQuery.value, forceRestart = true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun closeFilters() {
|
||||||
|
showFilters.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
fun reset() {
|
||||||
|
closeFilters()
|
||||||
|
search("")
|
||||||
|
}
|
||||||
|
|
||||||
private var searchJob: Job? = null
|
private var searchJob: Job? = null
|
||||||
fun search(query: String, forceRestart: Boolean = false) {
|
fun search(query: String, forceRestart: Boolean = false) {
|
||||||
if (searchQuery.value == query && !forceRestart) return
|
if (searchQuery.value == query && !forceRestart) return
|
||||||
|
if (searchQuery.value != query) {
|
||||||
|
showFilters.value = false
|
||||||
|
}
|
||||||
searchQuery.value = query
|
searchQuery.value = query
|
||||||
isSearchEmpty.value = query.isEmpty()
|
isSearchEmpty.value = query.isEmpty()
|
||||||
hiddenResults.value = emptyList()
|
hiddenResults.value = emptyList()
|
||||||
@ -145,7 +159,7 @@ class SearchVM : ViewModel(), KoinComponent {
|
|||||||
searchUiSettings.resultOrder.collectLatest { resultOrder ->
|
searchUiSettings.resultOrder.collectLatest { resultOrder ->
|
||||||
searchService.search(
|
searchService.search(
|
||||||
query,
|
query,
|
||||||
filters = filters,
|
filters = if (query.isEmpty()) filters.copy(apps = true) else filters,
|
||||||
).collectLatest { results ->
|
).collectLatest { results ->
|
||||||
var resultsList = withContext(Dispatchers.Default) {
|
var resultsList = withContext(Dispatchers.Default) {
|
||||||
listOfNotNull(
|
listOfNotNull(
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
package de.mm20.launcher2.ui.launcher.searchbar
|
package de.mm20.launcher2.ui.launcher.search.filters
|
||||||
|
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.horizontalScroll
|
import androidx.compose.foundation.horizontalScroll
|
||||||
@ -91,7 +91,7 @@ fun KeyboardFilterBar(filters: SearchFilters, onFiltersChange: (SearchFilters) -
|
|||||||
modifier = Modifier.size(FilterChipDefaults.IconSize)
|
modifier = Modifier.size(FilterChipDefaults.IconSize)
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
label = { Text("Apps") }
|
label = { Text(stringResource(R.string.search_filter_apps)) }
|
||||||
)
|
)
|
||||||
FilterChip(
|
FilterChip(
|
||||||
modifier = Modifier.padding(end = 8.dp),
|
modifier = Modifier.padding(end = 8.dp),
|
||||||
@ -0,0 +1,212 @@
|
|||||||
|
package de.mm20.launcher2.ui.launcher.search.filters
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.FlowRow
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.rememberScrollState
|
||||||
|
import androidx.compose.foundation.verticalScroll
|
||||||
|
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 androidx.compose.material3.FilterChip
|
||||||
|
import androidx.compose.material3.FilterChipDefaults
|
||||||
|
import androidx.compose.material3.HorizontalDivider
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import de.mm20.launcher2.search.SearchFilters
|
||||||
|
import de.mm20.launcher2.ui.R
|
||||||
|
import de.mm20.launcher2.ui.icons.Wikipedia
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun SearchFilters(
|
||||||
|
filters: SearchFilters,
|
||||||
|
onFiltersChange: (SearchFilters) -> Unit,
|
||||||
|
modifier: Modifier = Modifier
|
||||||
|
) {
|
||||||
|
val allCategoriesEnabled = filters.allCategoriesEnabled
|
||||||
|
Column(
|
||||||
|
modifier = modifier
|
||||||
|
.verticalScroll(rememberScrollState())
|
||||||
|
.padding(horizontal = 4.dp),
|
||||||
|
) {
|
||||||
|
FilterChip(
|
||||||
|
selected = filters.allowNetwork,
|
||||||
|
onClick = {
|
||||||
|
onFiltersChange(filters.copy(allowNetwork = !filters.allowNetwork))
|
||||||
|
},
|
||||||
|
leadingIcon = {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Rounded.Language,
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = Modifier.size(FilterChipDefaults.IconSize)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
label = { Text(stringResource(R.string.search_filter_online)) }
|
||||||
|
)
|
||||||
|
HorizontalDivider(modifier = Modifier.padding(vertical = 8.dp))
|
||||||
|
FlowRow {
|
||||||
|
FilterChip(
|
||||||
|
modifier = Modifier.padding(end = 16.dp),
|
||||||
|
selected = filters.apps && !allCategoriesEnabled,
|
||||||
|
onClick = {
|
||||||
|
onFiltersChange(filters.toggleApps())
|
||||||
|
},
|
||||||
|
leadingIcon = {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Rounded.Apps,
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = Modifier.size(FilterChipDefaults.IconSize)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
label = { Text(stringResource(R.string.search_filter_apps)) }
|
||||||
|
)
|
||||||
|
FilterChip(
|
||||||
|
modifier = Modifier.padding(end = 16.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 = 16.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 = 16.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 = 16.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 = 16.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 = 16.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 = 16.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)) }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
HorizontalDivider(modifier = Modifier.padding(vertical = 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)) }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package de.mm20.launcher2.ui.launcher.searchbar
|
package de.mm20.launcher2.ui.launcher.search.filters
|
||||||
|
|
||||||
import de.mm20.launcher2.search.SearchFilters
|
import de.mm20.launcher2.search.SearchFilters
|
||||||
|
|
||||||
@ -16,7 +16,6 @@ import androidx.compose.material3.FilledIconButton
|
|||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButtonDefaults
|
import androidx.compose.material3.IconButtonDefaults
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
@ -33,6 +32,7 @@ import de.mm20.launcher2.searchactions.actions.SearchAction
|
|||||||
import de.mm20.launcher2.ui.component.SearchBar
|
import de.mm20.launcher2.ui.component.SearchBar
|
||||||
import de.mm20.launcher2.ui.component.SearchBarLevel
|
import de.mm20.launcher2.ui.component.SearchBarLevel
|
||||||
import de.mm20.launcher2.ui.launcher.search.SearchVM
|
import de.mm20.launcher2.ui.launcher.search.SearchVM
|
||||||
|
import de.mm20.launcher2.ui.launcher.search.filters.KeyboardFilterBar
|
||||||
import de.mm20.launcher2.ui.launcher.sheets.LocalBottomSheetManager
|
import de.mm20.launcher2.ui.launcher.sheets.LocalBottomSheetManager
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@ -54,19 +54,14 @@ fun LauncherSearchBar(
|
|||||||
val focusManager = LocalFocusManager.current
|
val focusManager = LocalFocusManager.current
|
||||||
val focusRequester = remember { FocusRequester() }
|
val focusRequester = remember { FocusRequester() }
|
||||||
|
|
||||||
val sheetManager = LocalBottomSheetManager.current
|
|
||||||
|
|
||||||
val searchVM: SearchVM = viewModel()
|
val searchVM: SearchVM = viewModel()
|
||||||
val hiddenItemsButtonEnabled by searchVM.hiddenResultsButton.collectAsState(false)
|
|
||||||
|
|
||||||
val hiddenItems by searchVM.hiddenResults
|
|
||||||
|
|
||||||
LaunchedEffect(focused) {
|
LaunchedEffect(focused) {
|
||||||
if (focused) focusRequester.requestFocus()
|
if (focused) focusRequester.requestFocus()
|
||||||
else focusManager.clearFocus()
|
else focusManager.clearFocus()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isSearchOpen && WindowInsets.isImeVisible) {
|
if (isSearchOpen && !searchVM.showFilters.value && WindowInsets.isImeVisible) {
|
||||||
KeyboardFilterBar(
|
KeyboardFilterBar(
|
||||||
filters = searchVM.filters.value,
|
filters = searchVM.filters.value,
|
||||||
onFiltersChange = {
|
onFiltersChange = {
|
||||||
@ -89,8 +84,11 @@ fun LauncherSearchBar(
|
|||||||
exit = scaleOut(tween(100))
|
exit = scaleOut(tween(100))
|
||||||
) {
|
) {
|
||||||
FilledIconButton(
|
FilledIconButton(
|
||||||
onClick = { },
|
onClick = {
|
||||||
colors = IconButtonDefaults.iconButtonColors()
|
searchVM.showFilters.value = !searchVM.showFilters.value
|
||||||
|
},
|
||||||
|
colors = if (searchVM.showFilters.value) IconButtonDefaults.filledTonalIconButtonColors()
|
||||||
|
else IconButtonDefaults.iconButtonColors()
|
||||||
) {
|
) {
|
||||||
Box {
|
Box {
|
||||||
Icon(imageVector = Icons.Rounded.FilterAlt, contentDescription = null)
|
Icon(imageVector = Icons.Rounded.FilterAlt, contentDescription = null)
|
||||||
@ -98,7 +96,9 @@ fun LauncherSearchBar(
|
|||||||
!searchVM.filters.value.allCategoriesEnabled,
|
!searchVM.filters.value.allCategoriesEnabled,
|
||||||
enter = scaleIn(tween(100)),
|
enter = scaleIn(tween(100)),
|
||||||
exit = scaleOut(tween(100)),
|
exit = scaleOut(tween(100)),
|
||||||
modifier = Modifier.align(Alignment.BottomEnd).offset(-3.dp, -3.dp)
|
modifier = Modifier
|
||||||
|
.align(Alignment.BottomEnd)
|
||||||
|
.offset(-3.dp, -3.dp)
|
||||||
) {
|
) {
|
||||||
Badge(
|
Badge(
|
||||||
containerColor = MaterialTheme.colorScheme.tertiary,
|
containerColor = MaterialTheme.colorScheme.tertiary,
|
||||||
|
|||||||
@ -1,2 +0,0 @@
|
|||||||
package de.mm20.launcher2.ui.launcher.searchbar
|
|
||||||
|
|
||||||
@ -917,4 +917,5 @@
|
|||||||
<string name="clock_variant_outlined">Outlined</string>
|
<string name="clock_variant_outlined">Outlined</string>
|
||||||
<string name="search_filter_tools">Tools</string>
|
<string name="search_filter_tools">Tools</string>
|
||||||
<string name="search_filter_online">Online results</string>
|
<string name="search_filter_online">Online results</string>
|
||||||
|
<string name="search_filter_apps">Apps</string>
|
||||||
</resources>
|
</resources>
|
||||||
@ -172,7 +172,19 @@ internal class SearchServiceImpl(
|
|||||||
.withCustomLabels(customAttributesRepository)
|
.withCustomLabels(customAttributesRepository)
|
||||||
.collectLatest { r ->
|
.collectLatest { r ->
|
||||||
results.update {
|
results.update {
|
||||||
it.copy(other = r.toImmutableList())
|
it.copy(other = r
|
||||||
|
.filter {
|
||||||
|
filters.apps && it is Application ||
|
||||||
|
filters.shortcuts && it is AppShortcut ||
|
||||||
|
filters.contacts && it is Contact ||
|
||||||
|
filters.events && it is CalendarEvent ||
|
||||||
|
filters.files && it is File ||
|
||||||
|
filters.websites && it is Website ||
|
||||||
|
filters.articles && it is Article ||
|
||||||
|
filters.places && it is Location
|
||||||
|
}
|
||||||
|
.toImmutableList()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user