diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml index 529374ff4..117d3aa46 100644 --- a/.idea/codeStyles/Project.xml +++ b/.idea/codeStyles/Project.xml @@ -56,6 +56,7 @@ + diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml index 6e6eec114..79ee123c2 100644 --- a/.idea/codeStyles/codeStyleConfig.xml +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -1,6 +1,5 @@ \ No newline at end of file diff --git a/app/app/build.gradle.kts b/app/app/build.gradle.kts index 08e85d498..73d5ab1db 100644 --- a/app/app/build.gradle.kts +++ b/app/app/build.gradle.kts @@ -140,6 +140,7 @@ dependencies { implementation(project(":data:currencies")) implementation(project(":data:customattrs")) implementation(project(":data:searchable")) + implementation(project(":data:settings")) implementation(project(":data:plugins")) implementation(project(":data:themes")) implementation(project(":data:files")) diff --git a/app/app/src/main/java/de/mm20/launcher2/LauncherApplication.kt b/app/app/src/main/java/de/mm20/launcher2/LauncherApplication.kt index 5874ba4ea..1988b81fe 100644 --- a/app/app/src/main/java/de/mm20/launcher2/LauncherApplication.kt +++ b/app/app/src/main/java/de/mm20/launcher2/LauncherApplication.kt @@ -36,6 +36,7 @@ import de.mm20.launcher2.searchactions.searchActionsModule import de.mm20.launcher2.services.favorites.favoritesModule import de.mm20.launcher2.services.tags.servicesTagsModule import de.mm20.launcher2.services.widgets.widgetsServiceModule +import de.mm20.launcher2.settings.settingsModule import de.mm20.launcher2.themes.themesModule import de.mm20.launcher2.weather.weatherModule import kotlinx.coroutines.* @@ -82,6 +83,7 @@ class LauncherApplication : Application(), CoroutineScope, ImageLoaderFactory { preferencesModule, searchModule, searchActionsModule, + settingsModule, themesModule, unitConverterModule, weatherModule, diff --git a/app/ui/build.gradle.kts b/app/ui/build.gradle.kts index 4aed0e48f..83c6da28e 100644 --- a/app/ui/build.gradle.kts +++ b/app/ui/build.gradle.kts @@ -135,6 +135,7 @@ dependencies { implementation(project(":data:files")) implementation(project(":data:widgets")) implementation(project(":data:searchable")) + implementation(project(":data:settings")) implementation(project(":data:themes")) implementation(project(":data:wikipedia")) implementation(project(":services:badges")) diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/SearchColumn.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/SearchColumn.kt index 4f25ee4a9..8327a3054 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/SearchColumn.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/SearchColumn.kt @@ -16,18 +16,15 @@ import androidx.compose.foundation.lazy.LazyItemScope import androidx.compose.foundation.lazy.LazyListScope import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.lazy.rememberLazyListState +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.* import androidx.compose.foundation.rememberScrollState import androidx.compose.material.icons.Icons import androidx.compose.material.icons.rounded.Person import androidx.compose.material.icons.rounded.Star import androidx.compose.material.icons.rounded.Tag import androidx.compose.material.icons.rounded.Work -import androidx.compose.material3.FilterChip -import androidx.compose.material3.FilterChipDefaults -import androidx.compose.material3.Icon -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.OutlinedButton -import androidx.compose.material3.Text +import androidx.compose.material3.* import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue @@ -99,6 +96,7 @@ fun SearchColumn( val calculator by viewModel.calculatorResults val wikipedia by viewModel.articleResults val locations by viewModel.locationResults + val settings by viewModel.settingsResults val website by viewModel.websiteResults val hiddenResults by viewModel.hiddenResults val separateWorkProfile by viewModel.separateWorkProfile.collectAsState(true) @@ -254,6 +252,12 @@ fun SearchColumn( } else null, highlightedItem = bestMatch as? SavableSearchable ) + ListResults( + items = settings.toImmutableList(), + reverse = reverse, + key = "settings", + highlightedItem = bestMatch as? SavableSearchable + ) ListResults( before = if (missingShortcutsPermission && !isSearchEmpty) { { @@ -268,138 +272,137 @@ fun SearchColumn( OutlinedButton(onClick = { viewModel.disableAppShortcutSearch() }) { - Text( - stringResource(R.string.turn_off), - ) - } + 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, + items = appShortcuts.toImmutableList(), + reverse = reverse, + key = "shortcuts", + highlightedItem = bestMatch as? SavableSearchable + ) + for (conv in unitConverter) { + SingleResult { + UnitConverterItem(unitConverter = conv) } - for (calc in calculator) { - SingleResult { - CalculatorItem(calculator = calc) - } + } + 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), - ) - } + } + 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 = 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 = 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) + } + ) } + } 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) - } + } + 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), - ) - } + } + 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 - ) + } + ) + } + } else null, + items = files.toImmutableList(), + reverse = reverse, + key = "files", + highlightedItem = bestMatch as? SavableSearchable + ) } } - } diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/SearchVM.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/SearchVM.kt index 17647616e..3757da0bd 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/SearchVM.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/SearchVM.kt @@ -23,13 +23,14 @@ import de.mm20.launcher2.search.Article import de.mm20.launcher2.search.CalendarEvent import de.mm20.launcher2.search.Contact import de.mm20.launcher2.search.File +import de.mm20.launcher2.search.Location import de.mm20.launcher2.search.SavableSearchable import de.mm20.launcher2.search.SearchService import de.mm20.launcher2.search.Searchable -import de.mm20.launcher2.search.Location import de.mm20.launcher2.search.SearchFilters import de.mm20.launcher2.search.Website import de.mm20.launcher2.search.data.Calculator +import de.mm20.launcher2.search.data.PojoSettings import de.mm20.launcher2.search.data.UnitConverter import de.mm20.launcher2.searchable.SavableSearchableRepository import de.mm20.launcher2.searchactions.actions.SearchAction @@ -73,6 +74,7 @@ class SearchVM : ViewModel(), KoinComponent { val isSearchEmpty = mutableStateOf(true) val locationResults = mutableStateOf>(emptyList()) + val settingsResults = mutableStateOf>(emptyList()) val appResults = mutableStateOf>(emptyList()) val workAppResults = mutableStateOf>(emptyList()) val appShortcutResults = mutableStateOf>(emptyList()) @@ -177,6 +179,7 @@ class SearchVM : ViewModel(), KoinComponent { results.contacts, results.calendars, results.locations, + results.settings, results.wikipedia, results.websites, results.calculators, @@ -247,6 +250,7 @@ class SearchVM : ViewModel(), KoinComponent { val locations = mutableListOf() val website = mutableListOf() val actions = mutableListOf() + val settings = mutableListOf() for (r in resultsList) { when { r is SavableSearchable && hiddenKeys.contains(r.key) && !filters.hiddenItems -> { @@ -264,6 +268,7 @@ class SearchVM : ViewModel(), KoinComponent { r is Article -> articles.add(r) r is Location -> locations.add(r) r is SearchAction -> actions.add(r) + r is PojoSettings -> settings.add(r) } } @@ -280,7 +285,8 @@ class SearchVM : ViewModel(), KoinComponent { articles, website, files, - actions + actions, + settings, ).firstNotNullOfOrNull { it.firstOrNull() } } @@ -295,6 +301,7 @@ class SearchVM : ViewModel(), KoinComponent { websiteResults.value = website calculatorResults.value = calc unitConverterResults.value = unitConv + settingsResults.value = settings hiddenResults.value = hidden if (results.searchActions != null) searchActionResults.value = actions } diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/common/list/ListItem.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/common/list/ListItem.kt index dd7e3b436..7c37c942b 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/common/list/ListItem.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/common/list/ListItem.kt @@ -1,5 +1,8 @@ package de.mm20.launcher2.ui.launcher.search.common.list +import android.content.Context +import android.content.Intent +import androidx.compose.foundation.clickable import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.runtime.* @@ -9,12 +12,9 @@ import androidx.compose.ui.layout.boundsInWindow import androidx.compose.ui.layout.onGloballyPositioned import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.dp -import de.mm20.launcher2.search.AppShortcut -import de.mm20.launcher2.search.CalendarEvent -import de.mm20.launcher2.search.Contact -import de.mm20.launcher2.search.File -import de.mm20.launcher2.search.Location -import de.mm20.launcher2.search.SavableSearchable +import de.mm20.launcher2.ktx.tryStartActivity +import de.mm20.launcher2.search.* +import de.mm20.launcher2.search.data.PojoSettings import de.mm20.launcher2.ui.component.InnerCard import de.mm20.launcher2.ui.ktx.toPixels import de.mm20.launcher2.ui.launcher.search.calendar.CalendarItem @@ -23,8 +23,10 @@ import de.mm20.launcher2.ui.launcher.search.contacts.ContactItem import de.mm20.launcher2.ui.launcher.search.files.FileItem import de.mm20.launcher2.ui.launcher.search.listItemViewModel import de.mm20.launcher2.ui.launcher.search.location.LocationItem +import de.mm20.launcher2.ui.launcher.search.settings.SettingsItem import de.mm20.launcher2.ui.launcher.search.shortcut.AppShortcutItem import de.mm20.launcher2.ui.locals.LocalGridSettings +import de.mm20.launcher2.ui.settings.SettingsActivity @Composable fun ListItem( @@ -121,6 +123,21 @@ fun ListItem( ) } + is PojoSettings -> { + SettingsItem( + data = item, + modifier = Modifier + .fillMaxWidth() + .clickable { + if (!viewModel.launch(context, bounds)) { + when (item.specialId) { + PojoSettings.specialIdLauncher -> launchSettingsPage(context) + } + } + } + ) + } + is AppShortcut -> { AppShortcutItem( shortcut = item, @@ -141,4 +158,10 @@ fun ListItem( } } } +} + +private fun launchSettingsPage(context: Context) { + context.tryStartActivity(Intent(context, SettingsActivity::class.java)).apply { + Intent.FLAG_ACTIVITY_NEW_TASK + } } \ No newline at end of file diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/filters/KeyboardFilterBarItem.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/filters/KeyboardFilterBarItem.kt index 933498538..302d5160a 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/filters/KeyboardFilterBarItem.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/filters/KeyboardFilterBarItem.kt @@ -2,16 +2,7 @@ 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 androidx.compose.material.icons.rounded.* import de.mm20.launcher2.preferences.KeyboardFilterBarItem import de.mm20.launcher2.search.SearchFilters import de.mm20.launcher2.ui.R @@ -25,6 +16,7 @@ val KeyboardFilterBarItem.icon KeyboardFilterBarItem.Places -> Icons.Rounded.Place KeyboardFilterBarItem.Files -> Icons.Rounded.Description KeyboardFilterBarItem.Tools -> Icons.Rounded.Handyman + KeyboardFilterBarItem.Settings -> Icons.Rounded.Settings KeyboardFilterBarItem.Articles -> Icons.Rounded.Wikipedia KeyboardFilterBarItem.Websites -> Icons.Rounded.Public KeyboardFilterBarItem.Shortcuts -> Icons.Rounded.AppShortcut @@ -40,6 +32,7 @@ fun KeyboardFilterBarItem.getLabel(context: Context): String { 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.Settings -> context.getString(R.string.preference_search_settings) 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) @@ -63,6 +56,7 @@ fun SearchFilters.isSelected(item: KeyboardFilterBarItem): Boolean { KeyboardFilterBarItem.Places -> places KeyboardFilterBarItem.Files -> files KeyboardFilterBarItem.Tools -> tools + KeyboardFilterBarItem.Settings -> settings KeyboardFilterBarItem.Articles -> articles KeyboardFilterBarItem.Websites -> websites KeyboardFilterBarItem.Shortcuts -> shortcuts @@ -79,6 +73,7 @@ fun SearchFilters.toggle(item: KeyboardFilterBarItem): SearchFilters { KeyboardFilterBarItem.Places -> return togglePlaces() KeyboardFilterBarItem.Files -> return toggleFiles() KeyboardFilterBarItem.Tools -> return toggleTools() + KeyboardFilterBarItem.Settings -> return toggleSettings() KeyboardFilterBarItem.Articles -> return toggleArticles() KeyboardFilterBarItem.Websites -> return toggleWebsites() KeyboardFilterBarItem.Shortcuts -> return toggleShortcuts() diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/filters/SearchFilters.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/filters/SearchFilters.kt index c6b7142b6..a0a419fd0 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/filters/SearchFilters.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/filters/SearchFilters.kt @@ -7,16 +7,7 @@ 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.material.icons.rounded.* import androidx.compose.material3.FilterChip import androidx.compose.material3.FilterChipDefaults import androidx.compose.material3.HorizontalDivider @@ -212,6 +203,7 @@ fun SearchFilters( label = { Text(stringResource(R.string.preference_search_locations)) } ) FilterChip( + modifier = Modifier.padding(end = 16.dp), selected = filters.tools && (!allCategoriesEnabled || settings), onClick = { if (settings) { @@ -229,6 +221,25 @@ fun SearchFilters( }, label = { Text(stringResource(R.string.search_filter_tools)) } ) + FilterChip( + selected = filters.settings && (!allCategoriesEnabled || settings), + onClick = { + if (settings) { + onFiltersChange(filters.copy(settings = !filters.settings)) + } + else { + onFiltersChange(filters.toggleSettings()) + } + }, + leadingIcon = { + Icon( + imageVector = Icons.Rounded.Settings, + contentDescription = null, + modifier = Modifier.size(FilterChipDefaults.IconSize) + ) + }, + label = { Text(stringResource(R.string.preference_search_settings)) } + ) } HorizontalDivider(modifier = Modifier.padding(vertical = 8.dp)) FilterChip( diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/filters/SearchFiltersExt.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/filters/SearchFiltersExt.kt index 35e3f50e2..363e5c1f6 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/filters/SearchFiltersExt.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/filters/SearchFiltersExt.kt @@ -12,7 +12,8 @@ fun SearchFilters.withAllCategories(): SearchFilters { shortcuts = true, contacts = true, events = true, - tools = true + tools = true, + settings = true, ) } @@ -25,7 +26,8 @@ fun SearchFilters.withOnlyCategory( shortcuts: Boolean = false, contacts: Boolean = false, events: Boolean = false, - utilities: Boolean = false + utilities: Boolean = false, + settings: Boolean = false, ): SearchFilters { return copy( apps = apps, @@ -36,7 +38,8 @@ fun SearchFilters.withOnlyCategory( shortcuts = shortcuts, contacts = contacts, events = events, - tools = utilities + tools = utilities, + settings = settings, ) } @@ -144,3 +147,14 @@ fun SearchFilters.toggleTools(): SearchFilters { return copy(tools = !tools) } + +fun SearchFilters.toggleSettings(): SearchFilters { + if (allCategoriesEnabled) { + return withOnlyCategory(settings = true) + } + if (settings && enabledCategories == 1) { + return withAllCategories() + } + + return copy(settings = !settings) +} diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/settings/SettingsItem.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/settings/SettingsItem.kt new file mode 100644 index 000000000..2fec6c390 --- /dev/null +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/settings/SettingsItem.kt @@ -0,0 +1,57 @@ +package de.mm20.launcher2.ui.launcher.search.settings + +import androidx.compose.foundation.layout.* +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.dp +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import de.mm20.launcher2.search.data.PojoSettings +import de.mm20.launcher2.ui.R +import de.mm20.launcher2.ui.component.ShapedLauncherIcon +import de.mm20.launcher2.ui.launcher.search.common.SearchableItemVM +import de.mm20.launcher2.ui.launcher.search.listItemViewModel + +@Composable +fun SettingsItem( + data: PojoSettings, + modifier: Modifier = Modifier) +{ + val viewModel: SearchableItemVM = listItemViewModel(key = "search-${data.key}") + val icon by viewModel.icon.collectAsStateWithLifecycle() + + Row( + modifier = modifier.padding(8.dp) + ) { + Column { + ShapedLauncherIcon( + size = 48.dp, + icon = { icon }, + ) + } + Box(modifier = Modifier.width(16.dp)) + Column( + modifier = Modifier.align(Alignment.CenterVertically) + ) { + Row { + Text( + text = data.label, + style = MaterialTheme.typography.titleMedium, + overflow = TextOverflow.Ellipsis + ) + } + Row { + Text( + text = stringResource(R.string.preference_search_settings), + style = MaterialTheme.typography.bodySmall, + overflow = TextOverflow.Ellipsis + ) + } + } + } +} \ No newline at end of file diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/search/SearchSettingsScreen.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/search/SearchSettingsScreen.kt index 97b5333ae..47efd8d4b 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/search/SearchSettingsScreen.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/search/SearchSettingsScreen.kt @@ -5,23 +5,7 @@ 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.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.material.icons.rounded.* import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -177,6 +161,17 @@ fun SearchSettingsScreen() { enabled = hasAppShortcutsPermission == true ) + val settings by viewModel.settings.collectAsStateWithLifecycle(null) + SwitchPreference( + title = stringResource(R.string.preference_search_settings), + summary = stringResource(R.string.preference_search_settings_summary), + icon = Icons.Rounded.Settings, + value = settings == true, + onValueChanged = { + viewModel.setSettings(it) + } + ) + val calculator by viewModel.calculator.collectAsStateWithLifecycle(null) SwitchPreference( title = stringResource(R.string.preference_search_calculator), diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/search/SearchSettingsScreenVM.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/search/SearchSettingsScreenVM.kt index fbdd95d20..f6b9c6627 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/search/SearchSettingsScreenVM.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/search/SearchSettingsScreenVM.kt @@ -15,6 +15,7 @@ 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.SettingsSearchSettings import de.mm20.launcher2.preferences.search.ShortcutSearchSettings import de.mm20.launcher2.preferences.search.UnitConverterSettings import de.mm20.launcher2.preferences.search.WebsiteSearchSettings @@ -40,6 +41,7 @@ class SearchSettingsScreenVM : ViewModel(), KoinComponent { private val permissionsManager: PermissionsManager by inject() private val locationSearchSettings: LocationSearchSettings by inject() + private val settingsSearchSettings: SettingsSearchSettings by inject() val hasWorkProfile = mutableStateOf(false) @@ -116,6 +118,13 @@ class SearchSettingsScreenVM : ViewModel(), KoinComponent { locationSearchSettings.setEnabled(locations) } + val settings = settingsSearchSettings.enabled + .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) + + fun setSettings(settings: Boolean) { + settingsSearchSettings.setEnabled(settings) + } + val autoFocus = searchUiSettings.openKeyboard fun setAutoFocus(autoFocus: Boolean) { diff --git a/core/base/src/main/java/de/mm20/launcher2/search/SearchFilters.kt b/core/base/src/main/java/de/mm20/launcher2/search/SearchFilters.kt index 3edc61e3d..d9918d5bb 100644 --- a/core/base/src/main/java/de/mm20/launcher2/search/SearchFilters.kt +++ b/core/base/src/main/java/de/mm20/launcher2/search/SearchFilters.kt @@ -16,10 +16,11 @@ data class SearchFilters( val contacts: Boolean = true, val events: Boolean = true, val tools: Boolean = true, + val settings: Boolean = true, ) { val allCategoriesEnabled - get() = apps && websites && articles && places && files && shortcuts && contacts && events && tools + get() = apps && websites && articles && places && files && shortcuts && contacts && events && tools && settings val enabledCategories: Int - get() = apps.toInt() + websites.toInt() + articles.toInt() + places.toInt() + files.toInt() + shortcuts.toInt() + contacts.toInt() + events.toInt() + tools.toInt() + get() = apps.toInt() + websites.toInt() + articles.toInt() + places.toInt() + files.toInt() + shortcuts.toInt() + contacts.toInt() + events.toInt() + tools.toInt() + settings.toInt() } diff --git a/core/base/src/main/res/drawable/ic_settings_accessibility.xml b/core/base/src/main/res/drawable/ic_settings_accessibility.xml new file mode 100644 index 000000000..e69f333f9 --- /dev/null +++ b/core/base/src/main/res/drawable/ic_settings_accessibility.xml @@ -0,0 +1,10 @@ + + + diff --git a/core/base/src/main/res/drawable/ic_settings_airplane.xml b/core/base/src/main/res/drawable/ic_settings_airplane.xml new file mode 100644 index 000000000..c8b82d0e7 --- /dev/null +++ b/core/base/src/main/res/drawable/ic_settings_airplane.xml @@ -0,0 +1,12 @@ + + + diff --git a/core/base/src/main/res/drawable/ic_settings_apps.xml b/core/base/src/main/res/drawable/ic_settings_apps.xml new file mode 100644 index 000000000..a64191464 --- /dev/null +++ b/core/base/src/main/res/drawable/ic_settings_apps.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + diff --git a/core/base/src/main/res/drawable/ic_settings_battery.xml b/core/base/src/main/res/drawable/ic_settings_battery.xml new file mode 100644 index 000000000..61729c2c1 --- /dev/null +++ b/core/base/src/main/res/drawable/ic_settings_battery.xml @@ -0,0 +1,9 @@ + + + diff --git a/core/base/src/main/res/drawable/ic_settings_bluetooth.xml b/core/base/src/main/res/drawable/ic_settings_bluetooth.xml new file mode 100644 index 000000000..341d82ab3 --- /dev/null +++ b/core/base/src/main/res/drawable/ic_settings_bluetooth.xml @@ -0,0 +1,11 @@ + + + diff --git a/core/base/src/main/res/drawable/ic_settings_developer.xml b/core/base/src/main/res/drawable/ic_settings_developer.xml new file mode 100644 index 000000000..3a2e41199 --- /dev/null +++ b/core/base/src/main/res/drawable/ic_settings_developer.xml @@ -0,0 +1,11 @@ + + + diff --git a/core/base/src/main/res/drawable/ic_settings_deviceinfo.xml b/core/base/src/main/res/drawable/ic_settings_deviceinfo.xml new file mode 100644 index 000000000..8da08411c --- /dev/null +++ b/core/base/src/main/res/drawable/ic_settings_deviceinfo.xml @@ -0,0 +1,9 @@ + + + diff --git a/core/base/src/main/res/drawable/ic_settings_display.xml b/core/base/src/main/res/drawable/ic_settings_display.xml new file mode 100644 index 000000000..7991d13a6 --- /dev/null +++ b/core/base/src/main/res/drawable/ic_settings_display.xml @@ -0,0 +1,9 @@ + + + diff --git a/core/base/src/main/res/drawable/ic_settings_launcher.xml b/core/base/src/main/res/drawable/ic_settings_launcher.xml new file mode 100644 index 000000000..5aa11ecf0 --- /dev/null +++ b/core/base/src/main/res/drawable/ic_settings_launcher.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + diff --git a/core/base/src/main/res/drawable/ic_settings_location.xml b/core/base/src/main/res/drawable/ic_settings_location.xml new file mode 100644 index 000000000..6d2edde62 --- /dev/null +++ b/core/base/src/main/res/drawable/ic_settings_location.xml @@ -0,0 +1,10 @@ + + + diff --git a/core/base/src/main/res/drawable/ic_settings_nfc.xml b/core/base/src/main/res/drawable/ic_settings_nfc.xml new file mode 100644 index 000000000..688654910 --- /dev/null +++ b/core/base/src/main/res/drawable/ic_settings_nfc.xml @@ -0,0 +1,9 @@ + + + diff --git a/core/base/src/main/res/drawable/ic_settings_privacy.xml b/core/base/src/main/res/drawable/ic_settings_privacy.xml new file mode 100644 index 000000000..cdf5f44a6 --- /dev/null +++ b/core/base/src/main/res/drawable/ic_settings_privacy.xml @@ -0,0 +1,9 @@ + + + diff --git a/core/base/src/main/res/drawable/ic_settings_sound.xml b/core/base/src/main/res/drawable/ic_settings_sound.xml new file mode 100644 index 000000000..1c25c8df9 --- /dev/null +++ b/core/base/src/main/res/drawable/ic_settings_sound.xml @@ -0,0 +1,27 @@ + + + + + + + + + diff --git a/core/base/src/main/res/drawable/ic_settings_storage.xml b/core/base/src/main/res/drawable/ic_settings_storage.xml new file mode 100644 index 000000000..27afd798d --- /dev/null +++ b/core/base/src/main/res/drawable/ic_settings_storage.xml @@ -0,0 +1,22 @@ + + + + + + + diff --git a/core/base/src/main/res/drawable/ic_settings_tethering.xml b/core/base/src/main/res/drawable/ic_settings_tethering.xml new file mode 100644 index 000000000..343b7e12f --- /dev/null +++ b/core/base/src/main/res/drawable/ic_settings_tethering.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/core/base/src/main/res/drawable/ic_settings_updates.xml b/core/base/src/main/res/drawable/ic_settings_updates.xml new file mode 100644 index 000000000..6a96fd58c --- /dev/null +++ b/core/base/src/main/res/drawable/ic_settings_updates.xml @@ -0,0 +1,9 @@ + + + diff --git a/core/base/src/main/res/drawable/ic_settings_wifi.xml b/core/base/src/main/res/drawable/ic_settings_wifi.xml new file mode 100644 index 000000000..7d5f81639 --- /dev/null +++ b/core/base/src/main/res/drawable/ic_settings_wifi.xml @@ -0,0 +1,10 @@ + + + diff --git a/core/base/src/main/res/drawable/ic_settings_wireless.xml b/core/base/src/main/res/drawable/ic_settings_wireless.xml new file mode 100644 index 000000000..90974497a --- /dev/null +++ b/core/base/src/main/res/drawable/ic_settings_wireless.xml @@ -0,0 +1,9 @@ + + + diff --git a/core/i18n/src/main/res/values/strings.xml b/core/i18n/src/main/res/values/strings.xml index aeebc72bf..ea3eb0bde 100644 --- a/core/i18n/src/main/res/values/strings.xml +++ b/core/i18n/src/main/res/values/strings.xml @@ -664,6 +664,25 @@ Search radius Show a preview of a website if the search query is a URL Search the local area for shops and other places + Settings + Search for common settings pages for your device + Airplane mode + Device info + All apps + Storage + Wireless connection + Tethering + Accessibility + Battery + Sound + Display + Development + Security + System updates + Wi-Fi + Bluetooth + NFC + Location Show your own location in the map Web search Show shortcuts to different search engines diff --git a/core/preferences/src/main/java/de/mm20/launcher2/preferences/LauncherSettingsData.kt b/core/preferences/src/main/java/de/mm20/launcher2/preferences/LauncherSettingsData.kt index 3a8cc8c7d..0a7b848eb 100644 --- a/core/preferences/src/main/java/de/mm20/launcher2/preferences/LauncherSettingsData.kt +++ b/core/preferences/src/main/java/de/mm20/launcher2/preferences/LauncherSettingsData.kt @@ -158,9 +158,11 @@ data class LauncherSettingsData internal constructor( KeyboardFilterBarItem.Websites, KeyboardFilterBarItem.Places, KeyboardFilterBarItem.Tools, + KeyboardFilterBarItem.Settings, KeyboardFilterBarItem.HiddenResults, ), + val settingsSearchEnabled: Boolean = true, ) { constructor( @@ -408,5 +410,6 @@ enum class KeyboardFilterBarItem { @SerialName("contacts") Contacts, @SerialName("events") Events, @SerialName("tools") Tools, + @SerialName("settings") Settings, @SerialName("hidden") HiddenResults, } \ No newline at end of file diff --git a/core/preferences/src/main/java/de/mm20/launcher2/preferences/Module.kt b/core/preferences/src/main/java/de/mm20/launcher2/preferences/Module.kt index e19a509f3..84309b853 100644 --- a/core/preferences/src/main/java/de/mm20/launcher2/preferences/Module.kt +++ b/core/preferences/src/main/java/de/mm20/launcher2/preferences/Module.kt @@ -10,6 +10,7 @@ import de.mm20.launcher2.preferences.search.FileSearchSettings import de.mm20.launcher2.preferences.search.LocationSearchSettings import de.mm20.launcher2.preferences.search.RankingSettings import de.mm20.launcher2.preferences.search.SearchFilterSettings +import de.mm20.launcher2.preferences.search.SettingsSearchSettings import de.mm20.launcher2.preferences.search.ShortcutSearchSettings import de.mm20.launcher2.preferences.search.UnitConverterSettings import de.mm20.launcher2.preferences.search.WebsiteSearchSettings @@ -51,4 +52,5 @@ val preferencesModule = module { factory { ClockWidgetSettings(get()) } factory { LocationSearchSettings(get()) } factory { SearchFilterSettings(get()) } + factory { SettingsSearchSettings(get()) } } \ No newline at end of file diff --git a/core/preferences/src/main/java/de/mm20/launcher2/preferences/search/SettingsSearchSettings.kt b/core/preferences/src/main/java/de/mm20/launcher2/preferences/search/SettingsSearchSettings.kt new file mode 100644 index 000000000..df09f2ebf --- /dev/null +++ b/core/preferences/src/main/java/de/mm20/launcher2/preferences/search/SettingsSearchSettings.kt @@ -0,0 +1,30 @@ +package de.mm20.launcher2.preferences.search + +import de.mm20.launcher2.preferences.LauncherDataStore +import kotlinx.coroutines.flow.map + +class SettingsSearchSettings internal constructor( + private val launcherDataStore: LauncherDataStore, +) { + + val data + get() = launcherDataStore.data.map { + SettingsSearchSettingsData( + enabled = it.settingsSearchEnabled, + ) + } + + val enabled + get() = launcherDataStore.data.map { it.settingsSearchEnabled } + + fun setEnabled(enabled: Boolean) { + launcherDataStore.update { + it.copy(settingsSearchEnabled = enabled) + } + } + +} + +data class SettingsSearchSettingsData( + val enabled: Boolean = false, +) \ No newline at end of file diff --git a/data/settings/.gitignore b/data/settings/.gitignore new file mode 100644 index 000000000..42afabfd2 --- /dev/null +++ b/data/settings/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/data/settings/build.gradle.kts b/data/settings/build.gradle.kts new file mode 100644 index 000000000..fe55d0258 --- /dev/null +++ b/data/settings/build.gradle.kts @@ -0,0 +1,49 @@ +plugins { + alias(libs.plugins.android.library) + alias(libs.plugins.kotlin.android) +} + +android { + compileSdk = libs.versions.compileSdk.get().toInt() + + defaultConfig { + minSdk = libs.versions.minSdk.get().toInt() + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles("consumer-rules.pro") + } + + buildTypes { + release { + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + } + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = "1.8" + } + namespace = "de.mm20.launcher2.settings" +} + +dependencies { + implementation(libs.bundles.kotlin) + implementation(libs.androidx.core) + implementation(libs.androidx.appcompat) + + implementation(libs.bundles.androidx.lifecycle) + + implementation(libs.koin.android) + + implementation(project(":core:base")) + implementation(project(":core:preferences")) + implementation(project(":core:ktx")) + +} diff --git a/data/settings/consumer-rules.pro b/data/settings/consumer-rules.pro new file mode 100644 index 000000000..e69de29bb diff --git a/data/settings/proguard-rules.pro b/data/settings/proguard-rules.pro new file mode 100644 index 000000000..01639a198 --- /dev/null +++ b/data/settings/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle.kts.kts.kts. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/data/settings/src/main/AndroidManifest.xml b/data/settings/src/main/AndroidManifest.xml new file mode 100644 index 000000000..a5c402166 --- /dev/null +++ b/data/settings/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + / + \ No newline at end of file diff --git a/data/settings/src/main/java/de/mm20/launcher2/search/data/PojoSettings.kt b/data/settings/src/main/java/de/mm20/launcher2/search/data/PojoSettings.kt new file mode 100644 index 000000000..494156ef3 --- /dev/null +++ b/data/settings/src/main/java/de/mm20/launcher2/search/data/PojoSettings.kt @@ -0,0 +1,77 @@ +package de.mm20.launcher2.search.data + +import android.content.Context +import android.content.Intent +import android.os.Bundle +import androidx.annotation.DrawableRes +import androidx.core.content.ContextCompat +import de.mm20.launcher2.base.R +import de.mm20.launcher2.icons.ColorLayer +import de.mm20.launcher2.icons.StaticLauncherIcon +import de.mm20.launcher2.icons.TintedIconLayer +import de.mm20.launcher2.ktx.tryStartActivity +import de.mm20.launcher2.search.NullSerializer +import de.mm20.launcher2.search.SavableSearchable +import de.mm20.launcher2.search.SearchableSerializer + +data class PojoSettings( + @get:DrawableRes val icon: Int, + + val actionId: String? = null, + val specialId: String? = null, + val packageName: String? = null, + + override val key: String, + override val label: String +) : SavableSearchable { + override fun overrideLabel(label: String): SavableSearchable { + return copy(label = label) + } + + override fun launch(context: Context, options: Bundle?): Boolean { + return when { + specialId != null -> false + + packageName != null -> context.tryStartActivity( + Intent(actionId).apply { + setClassName(packageName, actionId!!) + flags = Intent.FLAG_ACTIVITY_NEW_TASK + }, options + ) + + else -> context.tryStartActivity( + Intent(actionId).apply { + flags = Intent.FLAG_ACTIVITY_NEW_TASK + }, options + ) + } + } + + override val preferDetailsOverLaunch: Boolean + get() = false + + override val domain: String + get() = Domain + + override fun getPlaceholderIcon(context: Context): StaticLauncherIcon { + val bgColor = R.color.teal + return StaticLauncherIcon( + foregroundLayer = TintedIconLayer( + icon = ContextCompat.getDrawable(context, icon)!!, + scale = 0.5f, + color = ContextCompat.getColor(context, bgColor) + ), + backgroundLayer = ColorLayer(ContextCompat.getColor(context, bgColor)) + ) + } + + override fun getSerializer(): SearchableSerializer { + return NullSerializer() + } + + companion object { + const val Domain: String = "settings" + + const val specialIdLauncher = "launcher-settings" + } +} \ No newline at end of file diff --git a/data/settings/src/main/java/de/mm20/launcher2/settings/Module.kt b/data/settings/src/main/java/de/mm20/launcher2/settings/Module.kt new file mode 100644 index 000000000..dab17b748 --- /dev/null +++ b/data/settings/src/main/java/de/mm20/launcher2/settings/Module.kt @@ -0,0 +1,8 @@ +package de.mm20.launcher2.settings + +import org.koin.android.ext.koin.androidContext +import org.koin.dsl.module + +val settingsModule = module { + single { SettingsRepositoryImpl(androidContext(), get()) } +} \ No newline at end of file diff --git a/data/settings/src/main/java/de/mm20/launcher2/settings/SettingsRepository.kt b/data/settings/src/main/java/de/mm20/launcher2/settings/SettingsRepository.kt new file mode 100644 index 000000000..7f03743e0 --- /dev/null +++ b/data/settings/src/main/java/de/mm20/launcher2/settings/SettingsRepository.kt @@ -0,0 +1,180 @@ +package de.mm20.launcher2.settings + +import android.content.Context +import android.content.Intent +import android.content.pm.PackageManager +import android.provider.Settings +import de.mm20.launcher2.preferences.search.SettingsSearchSettings +import de.mm20.launcher2.search.data.PojoSettings +import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.persistentListOf +import kotlinx.collections.immutable.toImmutableList +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.withContext +import org.koin.core.component.KoinComponent + +interface SettingsRepository { + fun search(query: String) : Flow> +} + +class SettingsRepositoryImpl( + private val context: Context, + private val settings: SettingsSearchSettings +) : SettingsRepository, KoinComponent { + + override fun search(query: String): Flow> { + return settings.enabled.map { + if (it && query.isNotBlank()) { + filteredSettingsList(query) + } else { + persistentListOf() + } + } + } + + private suspend fun filteredSettingsList(query: String): ImmutableList { + return withContext(Dispatchers.IO) { + getKnownSettingsList(context).filter { + context.getString(R.string.preference_search_settings).contains(query, ignoreCase = true) || + it.label.contains(query, ignoreCase = true) + }.toImmutableList() + } + } + private fun getKnownSettingsList(context: Context): List { + val pm = context.packageManager + val list = mutableListOf() + + list.addAll( + listOf( + PojoSettings( + icon = R.drawable.ic_settings_airplane, + actionId = Settings.ACTION_AIRPLANE_MODE_SETTINGS, + key = "settings-airplane", + label = context.getString(R.string.settings_airplane) + ), + PojoSettings( + icon = R.drawable.ic_settings_deviceinfo, + actionId = Settings.ACTION_DEVICE_INFO_SETTINGS, + key = "settings-device", + label = context.getString(R.string.settings_deviceinfo) + ), + PojoSettings( + icon = R.drawable.ic_settings_apps, + actionId = Settings.ACTION_MANAGE_APPLICATIONS_SETTINGS, + key = "settings-apps", + label = context.getString(R.string.settings_manage_applications) + ), + PojoSettings( + icon = R.drawable.ic_settings_storage, + actionId = Settings.ACTION_INTERNAL_STORAGE_SETTINGS, + key = "settings-storage", + label = context.getString(R.string.settings_storage) + ), + PojoSettings( + icon = R.drawable.ic_settings_wireless, + actionId = Settings.ACTION_WIRELESS_SETTINGS, + key = "settings-wireless", + label = context.getString(R.string.settings_wireless) + ), + PojoSettings( + icon = R.drawable.ic_settings_tethering, + actionId = "com.android.settings.TetherSettings", + packageName = "com.android.settings", + key = "settings-tethering", + label = context.getString(R.string.settings_tethering) + ), + PojoSettings( + icon = R.drawable.ic_settings_accessibility, + actionId = Settings.ACTION_ACCESSIBILITY_SETTINGS, + key = "settings-accessibility", + label = context.getString(R.string.settings_accessibility) + ), + PojoSettings( + icon = R.drawable.ic_settings_battery, + actionId = Intent.ACTION_POWER_USAGE_SUMMARY, + key = "settings-battery", + label = context.getString(R.string.settings_battery) + ), + PojoSettings( + icon = R.drawable.ic_settings_sound, + actionId = Settings.ACTION_SOUND_SETTINGS, + key = "settings-sound", + label = context.getString(R.string.settings_sound) + ), + PojoSettings( + icon = R.drawable.ic_settings_display, + actionId = Settings.ACTION_DISPLAY_SETTINGS, + key = "settings-display", + label = context.getString(R.string.settings_display) + ), + PojoSettings( + icon = R.drawable.ic_settings_developer, + actionId = Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS, + key = "settings-developer", + label = context.getString(R.string.settings_developer) + ), + PojoSettings( + icon = R.drawable.ic_settings_privacy, + actionId = Settings.ACTION_SECURITY_SETTINGS, + key = "settings-security", + label = context.getString(R.string.settings_security) + ), + PojoSettings( + icon = R.drawable.ic_settings_updates, + actionId = "android.settings.SYSTEM_UPDATE_SETTINGS", + key = "settings-updates", + label = context.getString(R.string.settings_updates) + ), + + + PojoSettings( + icon = R.drawable.ic_settings_launcher, + specialId = PojoSettings.specialIdLauncher, + key = "settings-launcher", + label = context.getString(R.string.app_name) + ), + ) + ) + + if (pm.hasSystemFeature(PackageManager.FEATURE_WIFI)) { + list.add(PojoSettings( + icon = R.drawable.ic_settings_wifi, + actionId = Settings.ACTION_WIFI_SETTINGS, + key = "settings-wifi", + label = context.getString(R.string.settings_wifi) + )) + } + + if (pm.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)) { + list.add(PojoSettings( + icon = R.drawable.ic_settings_bluetooth, + actionId = Settings.ACTION_BLUETOOTH_SETTINGS, + key = "settings-bluetooth", + label = context.getString(R.string.settings_bluetooth) + )) + } + + if (pm.hasSystemFeature(PackageManager.FEATURE_NFC)) { + list.add(PojoSettings( + icon = R.drawable.ic_settings_nfc, + actionId = Settings.ACTION_NFC_SETTINGS, + key = "settings-nfc", + label = context.getString(R.string.settings_nfc) + )) + } + + if (pm.hasSystemFeature(PackageManager.FEATURE_LOCATION)) { + list.add(PojoSettings( + icon = R.drawable.ic_settings_location, + actionId = Settings.ACTION_LOCATION_SOURCE_SETTINGS, + key = "settings-location", + label = context.getString(R.string.settings_location) + )) + } + + return list + } + +} \ No newline at end of file diff --git a/services/search/build.gradle.kts b/services/search/build.gradle.kts index fbb97dc58..0229b588c 100644 --- a/services/search/build.gradle.kts +++ b/services/search/build.gradle.kts @@ -47,6 +47,7 @@ dependencies { implementation(libs.coil.core) implementation(project(":data:calculator")) + implementation(project(":data:settings")) implementation(project(":data:unitconverter")) implementation(project(":data:customattrs")) implementation(project(":data:search-actions")) diff --git a/services/search/src/main/java/de/mm20/launcher2/search/Module.kt b/services/search/src/main/java/de/mm20/launcher2/search/Module.kt index a560c8898..d5dace2aa 100644 --- a/services/search/src/main/java/de/mm20/launcher2/search/Module.kt +++ b/services/search/src/main/java/de/mm20/launcher2/search/Module.kt @@ -15,6 +15,7 @@ val searchModule = module { get(named()), get(), get(), + get(), get(named()), get(), get(), diff --git a/services/search/src/main/java/de/mm20/launcher2/search/SearchService.kt b/services/search/src/main/java/de/mm20/launcher2/search/SearchService.kt index 99ecb29da..bc52052da 100644 --- a/services/search/src/main/java/de/mm20/launcher2/search/SearchService.kt +++ b/services/search/src/main/java/de/mm20/launcher2/search/SearchService.kt @@ -4,10 +4,12 @@ import de.mm20.launcher2.calculator.CalculatorRepository import de.mm20.launcher2.data.customattrs.CustomAttributesRepository import de.mm20.launcher2.data.customattrs.utils.withCustomLabels import de.mm20.launcher2.search.data.Calculator +import de.mm20.launcher2.search.data.PojoSettings import de.mm20.launcher2.search.data.UnitConverter import de.mm20.launcher2.searchactions.SearchActionService import de.mm20.launcher2.searchactions.actions.SearchAction import de.mm20.launcher2.unitconverter.UnitConverterRepository +import de.mm20.launcher2.settings.SettingsRepository import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList @@ -37,6 +39,7 @@ internal class SearchServiceImpl( private val locationRepository: SearchableRepository, private val unitConverterRepository: UnitConverterRepository, private val calculatorRepository: CalculatorRepository, + private val settingsRepository: SettingsRepository, private val websiteRepository: SearchableRepository, private val searchActionService: SearchActionService, private val customAttributesRepository: CustomAttributesRepository, @@ -119,6 +122,15 @@ internal class SearchServiceImpl( } } } + if (filters.settings) { + launch { + settingsRepository.search(query).collectLatest { r -> + results.update { + it.copy(settings = r) + } + } + } + } if (filters.websites) { launch { websiteRepository.search(query, filters.allowNetwork) @@ -207,6 +219,7 @@ data class SearchResults( val websites: ImmutableList? = null, val wikipedia: ImmutableList
? = null, val locations: ImmutableList? = null, + val settings: ImmutableList? = null, val searchActions: ImmutableList? = null, val other: ImmutableList? = null, ) @@ -222,6 +235,7 @@ fun SearchResults.toList(): List { unitConverters, websites, wikipedia, + settings, searchActions, other, ).flatten() diff --git a/settings.gradle.kts b/settings.gradle.kts index f9a180c09..a2a6f6545 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -44,6 +44,7 @@ include(":data:weather") include(":data:notifications") include(":data:search-actions") include(":data:searchable") +include(":data:settings") include(":data:plugins") include(":services:accounts")