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")