diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/common/SearchablePicker.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/common/SearchablePicker.kt new file mode 100644 index 00000000..66399ee4 --- /dev/null +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/common/SearchablePicker.kt @@ -0,0 +1,114 @@ +package de.mm20.launcher2.ui.common + +import androidx.compose.foundation.BorderStroke +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.rounded.CheckCircle +import androidx.compose.material.icons.rounded.Search +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.OutlinedTextField +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember +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 androidx.lifecycle.viewmodel.compose.viewModel +import de.mm20.launcher2.search.SavableSearchable +import de.mm20.launcher2.ui.R +import de.mm20.launcher2.ui.component.BottomSheetDialog +import de.mm20.launcher2.ui.component.ShapedLauncherIcon +import de.mm20.launcher2.ui.ktx.toPixels + +@Composable +fun SearchablePicker( + value: SavableSearchable?, + onValueChanged: (SavableSearchable?) -> Unit, + title: @Composable () -> Unit, + onDismissRequest: () -> Unit, +) { + val viewModel: SearchablePickerVM = viewModel() + + BottomSheetDialog(onDismissRequest = onDismissRequest, title = title) { + Column( + modifier = Modifier + .fillMaxSize() + .padding(it) + ) { + OutlinedTextField( + modifier = Modifier + .fillMaxWidth() + .padding(bottom = 12.dp), + value = viewModel.searchQuery, + onValueChange = { viewModel.onSearchQueryChanged(it) }, + leadingIcon = { + Icon(Icons.Rounded.Search, null) + }, + placeholder = { + Text(stringResource(R.string.search_bar_placeholder)) + } + ) + LazyColumn( + modifier = Modifier + .weight(1f) + .fillMaxWidth(), + ) { + items(viewModel.items) { + val iconSize = 32.dp.toPixels() + val icon by remember(it.key) { + viewModel.getIcon( + it, + iconSize.toInt() + ) + }.collectAsStateWithLifecycle(null) + val selected = it.key == value?.key + Surface( + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 4.dp), + shape = MaterialTheme.shapes.small, + border = BorderStroke(1.dp, if (selected) MaterialTheme.colorScheme.secondary else MaterialTheme.colorScheme.outline), + color = if (selected) MaterialTheme.colorScheme.secondaryContainer else MaterialTheme.colorScheme.surface, + onClick = { onValueChanged(it) }) { + Row( + modifier = Modifier.padding(16.dp), + verticalAlignment = Alignment.CenterVertically + ) { + ShapedLauncherIcon( + icon = { icon }, + size = 32.dp, + ) + Text( + text = it.label, + modifier = Modifier.weight(1f).padding(horizontal = 16.dp), + style = MaterialTheme.typography.labelMedium, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + ) + if (selected) { + Icon( + imageVector = Icons.Rounded.CheckCircle, + contentDescription = null, + tint = MaterialTheme.colorScheme.secondary, + ) + } + } + } + } + } + } + } + +} \ No newline at end of file diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/common/SearchablePickerVM.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/common/SearchablePickerVM.kt new file mode 100644 index 00000000..dad6f468 --- /dev/null +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/common/SearchablePickerVM.kt @@ -0,0 +1,66 @@ +package de.mm20.launcher2.ui.common + +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import de.mm20.launcher2.icons.IconRepository +import de.mm20.launcher2.icons.LauncherIcon +import de.mm20.launcher2.preferences.LauncherDataStore +import de.mm20.launcher2.preferences.Settings +import de.mm20.launcher2.search.SavableSearchable +import de.mm20.launcher2.search.SearchService +import de.mm20.launcher2.search.toList +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import org.koin.androidx.compose.inject +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject + +class SearchablePickerVM: ViewModel(), KoinComponent { + + private val dataStore: LauncherDataStore by inject() + private val searchService: SearchService by inject() + private val iconRepository: IconRepository by inject() + + var searchQuery by mutableStateOf("") + + init { + onSearchQueryChanged("", true) + } + + var items by mutableStateOf(emptyList()) + + var searchJob: Job? = null + fun onSearchQueryChanged(query: String, forceRestart: Boolean = false) { + if (searchQuery == query && !forceRestart) return + searchQuery = query + searchJob?.cancel() + searchJob = viewModelScope.launch { + val settings = dataStore.data.first() + searchService.search( + query = query, + shortcuts = settings.appShortcutSearch, + contacts = settings.contactsSearch, + calendars = settings.calendarSearch, + files = settings.fileSearch, + ).collectLatest { + if (searchQuery != query) return@collectLatest + items = withContext(Dispatchers.Default) { + it.toList().filterIsInstance().sorted() + } + } + } + } + + fun getIcon(searchable: SavableSearchable, size: Int): Flow { + return iconRepository.getIcon(searchable, size) + } +} \ No newline at end of file diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/gestures/GestureSettingsScreen.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/gestures/GestureSettingsScreen.kt index aed1d68b..fa6eb65d 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/gestures/GestureSettingsScreen.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/gestures/GestureSettingsScreen.kt @@ -2,23 +2,43 @@ package de.mm20.launcher2.ui.settings.gestures import androidx.appcompat.app.AppCompatActivity import androidx.compose.animation.AnimatedVisibility +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.material3.LocalContentColor +import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.livedata.observeAsState +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.alpha import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel +import de.mm20.launcher2.icons.LauncherIcon import de.mm20.launcher2.ktx.isAtLeastApiLevel import de.mm20.launcher2.preferences.Settings.GestureSettings.GestureAction import de.mm20.launcher2.preferences.Settings.LayoutSettings.Layout +import de.mm20.launcher2.search.SavableSearchable import de.mm20.launcher2.ui.R +import de.mm20.launcher2.ui.common.SearchablePicker import de.mm20.launcher2.ui.component.MissingPermissionBanner +import de.mm20.launcher2.ui.component.ShapedLauncherIcon import de.mm20.launcher2.ui.component.preferences.ListPreference +import de.mm20.launcher2.ui.component.preferences.Preference import de.mm20.launcher2.ui.component.preferences.PreferenceCategory import de.mm20.launcher2.ui.component.preferences.PreferenceScreen +import de.mm20.launcher2.ui.ktx.toPixels @Composable fun GestureSettingsScreen() { @@ -35,11 +55,13 @@ fun GestureSettingsScreen() { add(stringResource(R.string.gesture_action_recents) to GestureAction.OpenRecents) add(stringResource(R.string.gesture_action_power_menu) to GestureAction.OpenPowerDialog) add(stringResource(R.string.gesture_action_open_search) to GestureAction.OpenSearch) + add(stringResource(R.string.gesture_action_launch_app) to GestureAction.LaunchApp) } val context = LocalContext.current PreferenceScreen(title = stringResource(R.string.preference_screen_gestures)) { item { + val appIconSize = 32.dp.toPixels() PreferenceCategory { val doubleTap by viewModel.doubleTap.observeAsState() AnimatedVisibility(hasPermission == false && requiresAccessibilityService(doubleTap)) { @@ -49,12 +71,21 @@ fun GestureSettingsScreen() { onClick = { viewModel.requestPermission(context as AppCompatActivity) } ) } - ListPreference( + val doubleTapApp by viewModel.doubleTapApp.collectAsState(null) + val doubleTapAppIcon by remember(doubleTapApp?.key) { + viewModel.getIcon(doubleTapApp, appIconSize.toInt()) + }.collectAsState(null) + GesturePreference( title = stringResource(R.string.preference_gesture_double_tap), - items = options, value = doubleTap, - onValueChanged = { if (it != null) viewModel.setDoubleTap(it) } + onValueChanged = { viewModel.setDoubleTap(it) }, + isOpenSearch = false, + options = options, + app = doubleTapApp, + appIcon = doubleTapAppIcon, + onAppChanged = { viewModel.setDoubleTapApp(it) } ) + val longPress by viewModel.longPress.observeAsState() AnimatedVisibility(hasPermission == false && requiresAccessibilityService(longPress)) { MissingPermissionBanner( @@ -63,12 +94,21 @@ fun GestureSettingsScreen() { onClick = { viewModel.requestPermission(context as AppCompatActivity) } ) } - ListPreference( + val longPressApp by viewModel.longPressApp.collectAsState(null) + val longPressAppIcon by remember(longPressApp?.key) { + viewModel.getIcon(longPressApp, appIconSize.toInt()) + }.collectAsState(null) + GesturePreference( title = stringResource(R.string.preference_gesture_long_press), - items = options, value = longPress, - onValueChanged = { if (it != null) viewModel.setLongPress(it) } + onValueChanged = { viewModel.setLongPress(it) }, + isOpenSearch = false, + options = options, + app = longPressApp, + appIcon = longPressAppIcon, + onAppChanged = { viewModel.setLongPressApp(it) } ) + val swipeDown by viewModel.swipeDown.observeAsState() val swipeDownIsSearch = layout == Layout.PullDown AnimatedVisibility(hasPermission == false && requiresAccessibilityService(swipeDown) && !swipeDownIsSearch) { @@ -78,13 +118,21 @@ fun GestureSettingsScreen() { onClick = { viewModel.requestPermission(context as AppCompatActivity) } ) } - ListPreference( + val swipeDownApp by viewModel.swipeDownApp.collectAsState(null) + val swipeDownAppIcon by remember(swipeDownApp?.key) { + viewModel.getIcon(swipeDownApp, appIconSize.toInt()) + }.collectAsState(null) + GesturePreference( title = stringResource(R.string.preference_gesture_swipe_down), - enabled = !swipeDownIsSearch, - items = options, - value = if (swipeDownIsSearch) GestureAction.OpenSearch else swipeDown, - onValueChanged = { if (it != null) viewModel.setSwipeDown(it) } + value = swipeDown, + onValueChanged = { viewModel.setSwipeDown(it) }, + isOpenSearch = swipeDownIsSearch, + options = options, + app = swipeDownApp, + appIcon = swipeDownAppIcon, + onAppChanged = { viewModel.setSwipeDownApp(it) } ) + val swipeLeft by viewModel.swipeLeft.observeAsState() val swipeLeftIsSearch = layout == Layout.Pager AnimatedVisibility(hasPermission == false && requiresAccessibilityService(swipeLeft) && !swipeLeftIsSearch) { @@ -94,13 +142,21 @@ fun GestureSettingsScreen() { onClick = { viewModel.requestPermission(context as AppCompatActivity) } ) } - ListPreference( + val swipeLeftApp by viewModel.swipeLeftApp.collectAsState(null) + val swipeLeftAppIcon by remember(swipeLeftApp?.key) { + viewModel.getIcon(swipeLeftApp, appIconSize.toInt()) + }.collectAsState(null) + GesturePreference( title = stringResource(R.string.preference_gesture_swipe_left), - enabled = !swipeLeftIsSearch, - items = options, - value = if (swipeLeftIsSearch) GestureAction.OpenSearch else swipeLeft, - onValueChanged = { if (it != null) viewModel.setSwipeLeft(it) } + value = swipeLeft, + onValueChanged = { viewModel.setSwipeLeft(it) }, + isOpenSearch = swipeLeftIsSearch, + options = options, + app = swipeLeftApp, + appIcon = swipeLeftAppIcon, + onAppChanged = { viewModel.setSwipeLeftApp(it) } ) + val swipeRight by viewModel.swipeRight.observeAsState() val swipeRightIsSearch = layout == Layout.PagerReversed AnimatedVisibility(hasPermission == false && requiresAccessibilityService(swipeRight) && !swipeRightIsSearch) { @@ -110,20 +166,27 @@ fun GestureSettingsScreen() { onClick = { viewModel.requestPermission(context as AppCompatActivity) } ) } - ListPreference( + val swipeRightApp by viewModel.swipeRightApp.collectAsState(null) + val swipeRightAppIcon by remember(swipeRightApp?.key) { + viewModel.getIcon(swipeRightApp, appIconSize.toInt()) + }.collectAsState(null) + GesturePreference( title = stringResource(R.string.preference_gesture_swipe_right), - enabled = !swipeRightIsSearch, - items = options, - value = if (swipeRightIsSearch) GestureAction.OpenSearch else swipeRight, - onValueChanged = { if (it != null) viewModel.setSwipeRight(it) } + value = swipeRight, + onValueChanged = { viewModel.setSwipeRight(it) }, + isOpenSearch = swipeRightIsSearch, + options = options, + app = swipeRightApp, + appIcon = swipeRightAppIcon, + onAppChanged = { viewModel.setSwipeRightApp(it) } ) } } } } -fun requiresAccessibilityService(action: GestureAction?) : Boolean{ - return when(action) { +fun requiresAccessibilityService(action: GestureAction?): Boolean { + return when (action) { GestureAction.OpenNotificationDrawer, GestureAction.LockScreen, GestureAction.OpenQuickSettings, @@ -131,4 +194,64 @@ fun requiresAccessibilityService(action: GestureAction?) : Boolean{ GestureAction.OpenPowerDialog -> true else -> false } +} + +@Composable +fun GesturePreference( + title: String, + value: GestureAction?, + onValueChanged: (GestureAction) -> Unit, + isOpenSearch: Boolean, + options: List>, + app: SavableSearchable?, + appIcon: LauncherIcon?, + onAppChanged: (SavableSearchable?) -> Unit, +) { + var showAppPicker by remember { mutableStateOf(false) } + Row( + verticalAlignment = (Alignment.CenterVertically) + ) { + Box( + modifier = Modifier.weight(1f) + ) { + ListPreference( + title = title, + enabled = !isOpenSearch, + items = options, + value = if (isOpenSearch) GestureAction.OpenSearch else value, + onValueChanged = { if (it != null) onValueChanged(it) } + ) + } + + if (value == GestureAction.LaunchApp && !isOpenSearch) { + Box( + modifier = Modifier + .height(36.dp) + .width(1.dp) + .alpha(0.38f) + .background(LocalContentColor.current) + ) + Box(modifier = Modifier + .clickable { showAppPicker = true } + .padding(12.dp)) { + ShapedLauncherIcon(size = 32.dp, icon = { appIcon }) + } + } + } + + if (!isOpenSearch && value == GestureAction.LaunchApp && (showAppPicker || app == null)) { + SearchablePicker( + title = { Text(title) }, + onDismissRequest = { + showAppPicker = false + if (app == null) onValueChanged(GestureAction.None) + }, + value = app, + onValueChanged = { + showAppPicker = false + onAppChanged(it) + } + ) + } + } \ No newline at end of file diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/gestures/GestureSettingsScreenVM.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/gestures/GestureSettingsScreenVM.kt index b7f75010..d7dcf597 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/gestures/GestureSettingsScreenVM.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/gestures/GestureSettingsScreenVM.kt @@ -4,11 +4,21 @@ import androidx.appcompat.app.AppCompatActivity import androidx.lifecycle.ViewModel import androidx.lifecycle.asLiveData import androidx.lifecycle.viewModelScope +import de.mm20.launcher2.favorites.FavoritesRepository +import de.mm20.launcher2.icons.IconRepository +import de.mm20.launcher2.icons.LauncherIcon import de.mm20.launcher2.permissions.PermissionGroup import de.mm20.launcher2.permissions.PermissionsManager import de.mm20.launcher2.preferences.LauncherDataStore import de.mm20.launcher2.preferences.Settings.GestureSettings.GestureAction +import de.mm20.launcher2.search.SavableSearchable +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.WhileSubscribed +import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.shareIn +import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch import org.koin.core.component.KoinComponent import org.koin.core.component.inject @@ -16,6 +26,8 @@ import org.koin.core.component.inject class GestureSettingsScreenVM : ViewModel(), KoinComponent { private val dataStore: LauncherDataStore by inject() private val permissionsManager: PermissionsManager by inject() + private val favoritesRepository: FavoritesRepository by inject() + private val iconRepository: IconRepository by inject() val hasPermission = permissionsManager.hasPermission(PermissionGroup.Accessibility).asLiveData() @@ -72,7 +84,114 @@ class GestureSettingsScreenVM : ViewModel(), KoinComponent { } } + val swipeLeftApp: Flow = dataStore.data.map { it.gestures.swipeLeftApp } + .map { + if (it.isEmpty()) null else favoritesRepository.getFromKeys(listOf(it)).firstOrNull() + } + .stateIn(viewModelScope, SharingStarted.WhileSubscribed(stopTimeoutMillis = 10000), null) + + fun setSwipeLeftApp(searchable: SavableSearchable?) { + viewModelScope.launch { + searchable?.let { favoritesRepository.save(it) } + dataStore.updateData { + it.toBuilder() + .setGestures(it.gestures.toBuilder() + .setSwipeLeftApp(searchable?.key ?: "") + .build() + ) + .build() + } + } + } + + val swipeRightApp: Flow = dataStore.data.map { it.gestures.swipeRightApp } + .map { + if (it.isEmpty()) null else favoritesRepository.getFromKeys(listOf(it)).firstOrNull() + } + .stateIn(viewModelScope, SharingStarted.WhileSubscribed(stopTimeoutMillis = 10000), null) + + fun setSwipeRightApp(searchable: SavableSearchable?) { + viewModelScope.launch { + searchable?.let { favoritesRepository.save(it) } + dataStore.updateData { + it.toBuilder() + .setGestures(it.gestures.toBuilder() + .setSwipeRightApp(searchable?.key ?: "") + .build() + ) + .build() + } + } + } + + val swipeDownApp: Flow = dataStore.data.map { it.gestures.swipeDownApp } + .map { + if (it.isEmpty()) null else favoritesRepository.getFromKeys(listOf(it)).firstOrNull() + } + .stateIn(viewModelScope, SharingStarted.WhileSubscribed(stopTimeoutMillis = 10000), null) + + fun setSwipeDownApp(searchable: SavableSearchable?) { + viewModelScope.launch { + searchable?.let { favoritesRepository.save(it) } + dataStore.updateData { + it.toBuilder() + .setGestures(it.gestures.toBuilder() + .setSwipeDownApp(searchable?.key ?: "") + .build() + ) + .build() + } + } + } + + val longPressApp: Flow = dataStore.data.map { it.gestures.longPressApp } + .map { + if (it.isEmpty()) null else favoritesRepository.getFromKeys(listOf(it)).firstOrNull() + } + .stateIn(viewModelScope, SharingStarted.WhileSubscribed(stopTimeoutMillis = 10000), null) + + fun setLongPressApp(searchable: SavableSearchable?) { + viewModelScope.launch { + searchable?.let { favoritesRepository.save(it) } + dataStore.updateData { + it.toBuilder() + .setGestures(it.gestures.toBuilder() + .setLongPressApp(searchable?.key ?: "") + .build() + ) + .build() + } + } + } + + val doubleTapApp: Flow = dataStore.data.map { it.gestures.doubleTapApp } + .map { + if (it.isEmpty()) null else favoritesRepository.getFromKeys(listOf(it)).firstOrNull() + } + .stateIn(viewModelScope, SharingStarted.WhileSubscribed(stopTimeoutMillis = 10000), null) + + fun setDoubleTapApp(searchable: SavableSearchable?) { + viewModelScope.launch { + searchable?.let { favoritesRepository.save(it) } + dataStore.updateData { + it.toBuilder() + .setGestures(it.gestures.toBuilder() + .setDoubleTapApp(searchable?.key ?: "") + .build() + ) + .build() + } + } + } + + + fun requestPermission(context: AppCompatActivity) { permissionsManager.requestPermission(context, PermissionGroup.Accessibility) } + + fun getIcon(searchable: SavableSearchable?, size: Int): Flow { + if (searchable == null) return emptyFlow() + return iconRepository.getIcon(searchable, size) + } } \ No newline at end of file diff --git a/core/i18n/src/main/res/values/strings.xml b/core/i18n/src/main/res/values/strings.xml index d7911913..bb4ab121 100644 --- a/core/i18n/src/main/res/values/strings.xml +++ b/core/i18n/src/main/res/values/strings.xml @@ -750,6 +750,7 @@ Long press None Open search + Launch app Open notification drawer Turn off screen Open quick settings diff --git a/core/preferences/src/main/proto/settings.proto b/core/preferences/src/main/proto/settings.proto index 59cce7a4..9335fb87 100644 --- a/core/preferences/src/main/proto/settings.proto +++ b/core/preferences/src/main/proto/settings.proto @@ -313,12 +313,18 @@ message Settings { OpenQuickSettings = 4; OpenRecents = 5; OpenPowerDialog = 6; + LaunchApp = 7; } GestureAction swipe_down = 1; GestureAction swipe_left = 2; GestureAction swipe_right = 3; GestureAction double_tap = 4; GestureAction long_press = 5; + string swipe_down_app = 6; + string swipe_left_app = 7; + string swipe_right_app = 8; + string double_tap_app = 9; + string long_press_app = 10; } GestureSettings gestures = 28; } \ No newline at end of file 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 720b6654..ae2ba2ef 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 @@ -8,6 +8,7 @@ import de.mm20.launcher2.contacts.ContactRepository import de.mm20.launcher2.data.customattrs.CustomAttributesRepository import de.mm20.launcher2.data.customattrs.utils.withCustomLabels import de.mm20.launcher2.files.FileRepository +import de.mm20.launcher2.preferences.Settings import de.mm20.launcher2.preferences.Settings.AppShortcutSearchSettings import de.mm20.launcher2.preferences.Settings.CalculatorSearchSettings import de.mm20.launcher2.preferences.Settings.CalendarSearchSettings @@ -50,14 +51,34 @@ import kotlinx.coroutines.supervisorScope interface SearchService { fun search( query: String, - shortcuts: AppShortcutSearchSettings, - contacts: ContactsSearchSettings, - calendars: CalendarSearchSettings, - files: FilesSearchSettings, - calculator: CalculatorSearchSettings, - unitConverter: UnitConverterSearchSettings, - websites: WebsiteSearchSettings, - wikipedia: WikipediaSearchSettings, + shortcuts: AppShortcutSearchSettings = Settings.AppShortcutSearchSettings.newBuilder() + .setEnabled(false) + .build(), + contacts: ContactsSearchSettings = Settings.ContactsSearchSettings.newBuilder() + .setEnabled(false) + .build(), + calendars: CalendarSearchSettings = Settings.CalendarSearchSettings.newBuilder() + .setEnabled(false) + .build(), + files: FilesSearchSettings = Settings.FilesSearchSettings.newBuilder() + .setLocalFiles(false) + .setGdrive(false) + .setOnedrive(false) + .setOwncloud(false) + .setNextcloud(false) + .build(), + calculator: CalculatorSearchSettings = Settings.CalculatorSearchSettings.newBuilder() + .setEnabled(false) + .build(), + unitConverter: UnitConverterSearchSettings = Settings.UnitConverterSearchSettings.newBuilder() + .setEnabled(false) + .build(), + websites: WebsiteSearchSettings = Settings.WebsiteSearchSettings.newBuilder() + .setEnabled(false) + .build(), + wikipedia: WikipediaSearchSettings = Settings.WikipediaSearchSettings.newBuilder() + .setEnabled(false) + .build(), ): Flow } @@ -245,4 +266,20 @@ data class SearchResults( val wikipedia: ImmutableList? = null, val searchActions: ImmutableList? = null, val other: ImmutableList? = null, -) \ No newline at end of file +) + +fun SearchResults.toList(): List { + return listOfNotNull( + apps, + shortcuts, + contacts, + calendars, + files, + calculators, + unitConverters, + websites, + wikipedia, + searchActions, + other, + ).flatten() +} \ No newline at end of file