Simplify calendar search settings when no plugins are installed

This commit is contained in:
MM20 2025-03-24 23:31:16 +01:00
parent dfb598b24b
commit b4056df5c2
No known key found for this signature in database
GPG Key ID: 0B61A8F2DEAFA389
9 changed files with 263 additions and 150 deletions

View File

@ -38,6 +38,7 @@ import de.mm20.launcher2.ui.settings.about.AboutSettingsScreen
import de.mm20.launcher2.ui.settings.appearance.AppearanceSettingsScreen import de.mm20.launcher2.ui.settings.appearance.AppearanceSettingsScreen
import de.mm20.launcher2.ui.settings.backup.BackupSettingsScreen import de.mm20.launcher2.ui.settings.backup.BackupSettingsScreen
import de.mm20.launcher2.ui.settings.buildinfo.BuildInfoSettingsScreen import de.mm20.launcher2.ui.settings.buildinfo.BuildInfoSettingsScreen
import de.mm20.launcher2.ui.settings.calendarsearch.CalendarProviderSettingsScreen
import de.mm20.launcher2.ui.settings.calendarsearch.CalendarSearchSettingsScreen import de.mm20.launcher2.ui.settings.calendarsearch.CalendarSearchSettingsScreen
import de.mm20.launcher2.ui.settings.cards.CardsSettingsScreen import de.mm20.launcher2.ui.settings.cards.CardsSettingsScreen
import de.mm20.launcher2.ui.settings.colorscheme.ThemeSettingsScreen import de.mm20.launcher2.ui.settings.colorscheme.ThemeSettingsScreen
@ -198,6 +199,11 @@ class SettingsActivity : BaseActivity() {
composable("settings/search/calendar") { composable("settings/search/calendar") {
CalendarSearchSettingsScreen() CalendarSearchSettingsScreen()
} }
composable("settings/search/calendar/{providerId}") {
CalendarProviderSettingsScreen(
it.arguments?.getString("providerId") ?: return@composable
)
}
composable("settings/search/searchactions") { composable("settings/search/searchactions") {
SearchActionsSettingsScreen() SearchActionsSettingsScreen()
} }

View File

@ -0,0 +1,97 @@
package de.mm20.launcher2.ui.settings.calendarsearch
import android.app.PendingIntent
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.ErrorOutline
import androidx.compose.material3.CheckboxDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewmodel.compose.viewModel
import de.mm20.launcher2.calendar.providers.CalendarList
import de.mm20.launcher2.crashreporter.CrashReporter
import de.mm20.launcher2.ktx.sendWithBackgroundPermission
import de.mm20.launcher2.plugin.PluginState
import de.mm20.launcher2.themes.atTone
import de.mm20.launcher2.ui.R
import de.mm20.launcher2.ui.component.Banner
import de.mm20.launcher2.ui.component.preferences.CheckboxPreference
import de.mm20.launcher2.ui.component.preferences.PreferenceCategory
import de.mm20.launcher2.ui.component.preferences.PreferenceScreen
import de.mm20.launcher2.ui.component.preferences.SwitchPreference
import de.mm20.launcher2.ui.locals.LocalDarkTheme
@Composable
fun CalendarProviderSettingsScreen(providerId: String) {
val viewModel = viewModel<CalendarProviderSettingsScreenVM>()
LaunchedEffect(providerId) {
viewModel.init(providerId)
}
val enabled by viewModel.isProviderEnabled.collectAsStateWithLifecycle(false)
val calendarLists by viewModel.calendarLists.collectAsStateWithLifecycle(sortedMapOf<String, List<CalendarList>>())
val excludedCalendars by viewModel.excludedCalendars.collectAsStateWithLifecycle(emptySet())
val pluginState by viewModel.pluginState.collectAsStateWithLifecycle(null)
val providerAvailable = providerId == "local" || pluginState != null
PreferenceScreen(
title = pluginState?.plugin?.label ?: stringResource(R.string.preference_search_calendar)
) {
if (!providerAvailable) {
return@PreferenceScreen
}
item {
PreferenceCategory {
SwitchPreference(
title =
if (providerId == "local") stringResource(R.string.preference_search_calendar)
else pluginState?.plugin?.label ?: "",
summary =
if (providerId == "local") stringResource(R.string.preference_search_local_calendar_summary)
else (pluginState?.state as? PluginState.Ready)?.text
?: pluginState?.plugin?.description,
value = enabled && (pluginState == null || pluginState?.state is PluginState.Ready),
onValueChanged = { viewModel.setProviderEnabled(providerId, it) }
)
}
}
items(calendarLists.toList()) { (k, v) ->
PreferenceCategory(
title = k,
) {
for (list in v) {
CheckboxPreference(
title = list.name,
value = !excludedCalendars.contains(list.id),
onValueChanged = { viewModel.setCalendarExcluded(list.id, !it) },
checkboxColors = CheckboxDefaults.colors(
checkedColor = if (list.color == 0) MaterialTheme.colorScheme.primary
else Color(
list.color.atTone(if (LocalDarkTheme.current) 80 else 40)
),
checkmarkColor = if (list.color == 0) MaterialTheme.colorScheme.onPrimary
else Color(
list.color.atTone(if (LocalDarkTheme.current) 20 else 100)
)
),
enabled = enabled,
)
}
}
}
}
}

View File

@ -0,0 +1,42 @@
package de.mm20.launcher2.ui.settings.calendarsearch
import androidx.lifecycle.ViewModel
import de.mm20.launcher2.calendar.CalendarRepository
import de.mm20.launcher2.permissions.PermissionGroup
import de.mm20.launcher2.permissions.PermissionsManager
import de.mm20.launcher2.plugins.PluginService
import de.mm20.launcher2.preferences.search.CalendarSearchSettings
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.map
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
class CalendarProviderSettingsScreenVM: ViewModel(), KoinComponent {
private val providerId = MutableStateFlow<String>("")
fun init(providerId: String) {
this.providerId.value = providerId
}
private val calendarSearchSettings: CalendarSearchSettings by inject()
private val calendarRepository: CalendarRepository by inject()
private val pluginService: PluginService by inject()
val pluginState = providerId.flatMapLatest { pluginService.getPluginWithState(it) }
val isProviderEnabled = providerId.flatMapLatest { calendarSearchSettings.isProviderEnabled(it) }
fun setProviderEnabled(providerId: String, enabled: Boolean) {
calendarSearchSettings.setProviderEnabled(providerId, enabled)
}
val calendarLists = providerId
.flatMapLatest { calendarRepository.getCalendars(it) }
.map { it.groupBy { it.owner }.toSortedMap(compareBy { it }) }
val excludedCalendars = calendarSearchSettings.excludedCalendars
fun setCalendarExcluded(calendarId: String, excluded: Boolean) {
calendarSearchSettings.setCalendarExcluded(calendarId, excluded)
}
}

View File

@ -1,32 +1,21 @@
package de.mm20.launcher2.ui.settings.calendarsearch package de.mm20.launcher2.ui.settings.calendarsearch
import android.app.PendingIntent import android.app.PendingIntent
import android.util.Log
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.layout.padding 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.Icons
import androidx.compose.material.icons.rounded.CalendarToday
import androidx.compose.material.icons.rounded.Checklist
import androidx.compose.material.icons.rounded.ErrorOutline import androidx.compose.material.icons.rounded.ErrorOutline
import androidx.compose.material3.CheckboxDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ModalBottomSheet
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TextButton import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.pluralStringResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.lifecycle.Lifecycle import androidx.lifecycle.Lifecycle
@ -35,163 +24,83 @@ import androidx.lifecycle.viewmodel.compose.viewModel
import de.mm20.launcher2.crashreporter.CrashReporter import de.mm20.launcher2.crashreporter.CrashReporter
import de.mm20.launcher2.ktx.sendWithBackgroundPermission import de.mm20.launcher2.ktx.sendWithBackgroundPermission
import de.mm20.launcher2.plugin.PluginState import de.mm20.launcher2.plugin.PluginState
import de.mm20.launcher2.search.calendar.CalendarListType
import de.mm20.launcher2.themes.atTone
import de.mm20.launcher2.ui.R import de.mm20.launcher2.ui.R
import de.mm20.launcher2.ui.component.Banner import de.mm20.launcher2.ui.component.Banner
import de.mm20.launcher2.ui.component.MissingPermissionBanner import de.mm20.launcher2.ui.component.MissingPermissionBanner
import de.mm20.launcher2.ui.component.preferences.CheckboxPreference
import de.mm20.launcher2.ui.component.preferences.PreferenceCategory import de.mm20.launcher2.ui.component.preferences.PreferenceCategory
import de.mm20.launcher2.ui.component.preferences.PreferenceScreen import de.mm20.launcher2.ui.component.preferences.PreferenceScreen
import de.mm20.launcher2.ui.component.preferences.PreferenceWithSwitch import de.mm20.launcher2.ui.component.preferences.PreferenceWithSwitch
import de.mm20.launcher2.ui.locals.LocalDarkTheme import de.mm20.launcher2.ui.locals.LocalNavController
@Composable @Composable
fun CalendarSearchSettingsScreen() { fun CalendarSearchSettingsScreen() {
val viewModel: CalendarSearchSettingsScreenVM = viewModel() val viewModel: CalendarSearchSettingsScreenVM = viewModel()
val context = LocalContext.current val context = LocalContext.current
val navController = LocalNavController.current
val hasCalendarPermission by viewModel.hasCalendarPermission.collectAsState(null) val hasCalendarPermission by viewModel.hasCalendarPermission.collectAsState(null)
val plugins by viewModel.availablePlugins.collectAsState(emptyList()) val plugins by viewModel.availablePlugins.collectAsStateWithLifecycle(emptyList(), minActiveState = Lifecycle.State.RESUMED)
val enabledProviders by viewModel.enabledProviders.collectAsState(emptySet()) val enabledProviders by viewModel.enabledProviders.collectAsState(emptySet())
val calendarLists by viewModel.calendarLists.collectAsStateWithLifecycle(
null,
minActiveState = Lifecycle.State.RESUMED
)
val excludedCalendars by viewModel.excludedCalendars.collectAsState(emptyList())
var showDialogForProvider by remember { mutableStateOf<String?>(null) }
PreferenceScreen(title = stringResource(R.string.preference_search_calendar)) { PreferenceScreen(title = stringResource(R.string.preference_search_calendar)) {
item { item {
AnimatedVisibility(hasCalendarPermission == false) { PreferenceCategory {
MissingPermissionBanner( AnimatedVisibility(hasCalendarPermission == false) {
text = stringResource(R.string.missing_permission_calendar_search_settings), MissingPermissionBanner(
onClick = { text = stringResource(R.string.missing_permission_calendar_search_settings),
viewModel.requestCalendarPermission(context as AppCompatActivity) onClick = {
}, viewModel.requestCalendarPermission(context as AppCompatActivity)
modifier = Modifier.padding(16.dp) },
) modifier = Modifier.padding(16.dp)
}
val selectedCalendars = remember(excludedCalendars, calendarLists) {
calendarLists?.count { it.providerId == "local" }
?.minus(excludedCalendars.count {
it.startsWith("local:")
})
}
PreferenceWithSwitch(
title = stringResource(R.string.preference_search_calendar),
summary = if (selectedCalendars != null && calendarLists != null) "$selectedCalendars lists selected"
else stringResource(R.string.preference_search_calendar_summary),
switchValue = enabledProviders.contains("local") && hasCalendarPermission == true,
onSwitchChanged = {
viewModel.setProviderEnabled("local", it)
},
enabled = hasCalendarPermission == true,
onClick = {
showDialogForProvider = "local"
}
)
for (plugin in plugins) {
val state = plugin.state
if (state is PluginState.SetupRequired) {
Banner(
modifier = Modifier.padding(16.dp),
text = state.message
?: stringResource(id = R.string.plugin_state_setup_required),
icon = Icons.Rounded.ErrorOutline,
primaryAction = {
TextButton(onClick = {
try {
state.setupActivity.sendWithBackgroundPermission(context)
} catch (e: PendingIntent.CanceledException) {
CrashReporter.logException(e)
}
}) {
Text(stringResource(id = R.string.plugin_action_setup))
}
}
) )
} }
val selectedCalendars = remember(excludedCalendars, calendarLists) {
calendarLists?.count { it.providerId == plugin.plugin.authority }
?.minus(excludedCalendars.count {
it.startsWith(
"${plugin.plugin.authority}:"
)
})
}
PreferenceWithSwitch( PreferenceWithSwitch(
title = plugin.plugin.label, title = stringResource(R.string.preference_search_calendar),
enabled = state is PluginState.Ready, summary = stringResource(R.string.preference_search_local_calendar_summary),
summary = (state as? PluginState.SetupRequired)?.message switchValue = enabledProviders.contains("local") && hasCalendarPermission == true,
?: if (selectedCalendars != null && calendarLists != null) {
pluralStringResource(
R.plurals.calendar_search_enabled_lists,
selectedCalendars,
selectedCalendars
)
}
else (state as? PluginState.Ready)?.text ?: plugin.plugin.description,
switchValue = enabledProviders.contains(plugin.plugin.authority) && state is PluginState.Ready,
onSwitchChanged = { onSwitchChanged = {
viewModel.setProviderEnabled(plugin.plugin.authority, it) viewModel.setProviderEnabled("local", it)
}, },
enabled = hasCalendarPermission == true,
onClick = { onClick = {
showDialogForProvider = plugin.plugin.authority navController?.navigate("settings/search/calendar/local")
} }
) )
} for (plugin in plugins) {
} val state = plugin.state
} if (state is PluginState.SetupRequired) {
Banner(
Log.d("MM20", "${calendarLists.toString()}") modifier = Modifier.padding(16.dp),
text = state.message
val dialogCalendarLists by remember { ?: stringResource(id = R.string.plugin_state_setup_required),
derivedStateOf { icon = Icons.Rounded.ErrorOutline,
if (showDialogForProvider == null) null primaryAction = {
else calendarLists?.filter { it.providerId == showDialogForProvider } TextButton(onClick = {
} try {
} state.setupActivity.sendWithBackgroundPermission(context)
} catch (e: PendingIntent.CanceledException) {
if (showDialogForProvider != null && dialogCalendarLists != null) { CrashReporter.logException(e)
ModalBottomSheet( }
onDismissRequest = { }) {
showDialogForProvider = null Text(stringResource(id = R.string.plugin_action_setup))
}, }
) { }
val groups = remember(dialogCalendarLists) { )
dialogCalendarLists!!.groupBy { it.owner }.entries.sortedBy { it.key }
}
LazyColumn {
items(groups) {
PreferenceCategory(
title = it.key,
iconPadding = false,
) {
for (list in it.value) {
CheckboxPreference(
title = list.name,
iconPadding = false,
value = list.id !in excludedCalendars,
onValueChanged = { value ->
viewModel.setCalendarExcluded(list.id, !value)
},
checkboxColors = CheckboxDefaults.colors(
checkedColor = if (list.color == 0) MaterialTheme.colorScheme.primary
else Color(
list.color.atTone(if (LocalDarkTheme.current) 80 else 40)
),
checkmarkColor = if (list.color == 0) MaterialTheme.colorScheme.onPrimary
else Color(
list.color.atTone(if (LocalDarkTheme.current) 20 else 100)
)
)
)
}
} }
PreferenceWithSwitch(
title = plugin.plugin.label,
enabled = state is PluginState.Ready,
summary = (state as? PluginState.SetupRequired)?.message
?: (state as? PluginState.Ready)?.text
?: plugin.plugin.description,
switchValue = enabledProviders.contains(plugin.plugin.authority) && state is PluginState.Ready,
onSwitchChanged = {
viewModel.setProviderEnabled(plugin.plugin.authority, it)
},
onClick = {
navController?.navigate("settings/search/calendar/${plugin.plugin.authority}")
}
)
} }
} }
} }

View File

@ -25,6 +25,7 @@ import androidx.compose.material.icons.rounded.Warning
import androidx.compose.material.icons.rounded.Work import androidx.compose.material.icons.rounded.Work
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
@ -49,6 +50,7 @@ import de.mm20.launcher2.ui.component.preferences.PreferenceScreen
import de.mm20.launcher2.ui.component.preferences.PreferenceWithSwitch import de.mm20.launcher2.ui.component.preferences.PreferenceWithSwitch
import de.mm20.launcher2.ui.component.preferences.SwitchPreference import de.mm20.launcher2.ui.component.preferences.SwitchPreference
import de.mm20.launcher2.icons.Wikipedia import de.mm20.launcher2.icons.Wikipedia
import de.mm20.launcher2.plugin.PluginType
import de.mm20.launcher2.ui.launcher.search.filters.SearchFilters import de.mm20.launcher2.ui.launcher.search.filters.SearchFilters
import de.mm20.launcher2.ui.locals.LocalNavController import de.mm20.launcher2.ui.locals.LocalNavController
@ -65,6 +67,11 @@ fun SearchSettingsScreen() {
mutableStateOf(false) mutableStateOf(false)
} }
val plugins by viewModel.plugins.collectAsStateWithLifecycle(emptyList())
val hasCalendarPlugins by remember { derivedStateOf { plugins.any { it.plugin.type == PluginType.Calendar } } }
val hasLocationPlugins by remember { derivedStateOf { plugins.any { it.plugin.type == PluginType.LocationSearch } } }
PreferenceScreen(title = stringResource(R.string.preference_screen_search)) { PreferenceScreen(title = stringResource(R.string.preference_screen_search)) {
item { item {
@ -116,14 +123,43 @@ fun SearchSettingsScreen() {
enabled = hasContactsPermission == true enabled = hasContactsPermission == true
) )
Preference( if (hasCalendarPlugins) {
title = stringResource(R.string.preference_search_calendar), Preference(
summary = stringResource(R.string.preference_search_calendar_summary), title = stringResource(R.string.preference_search_calendar),
icon = Icons.Rounded.Today, summary = stringResource(R.string.preference_search_calendar_summary),
onClick = { icon = Icons.Rounded.Today,
navController?.navigate("settings/search/calendar") onClick = {
}, navController?.navigate("settings/search/calendar")
) },
)
} else {
val hasCalendarPermission by viewModel.hasCalendarPermission.collectAsStateWithLifecycle(
null
)
val calendar by viewModel.calendarSearch.collectAsStateWithLifecycle(null)
AnimatedVisibility(hasCalendarPermission == false) {
MissingPermissionBanner(
text = stringResource(R.string.missing_permission_calendar_search_settings),
onClick = {
viewModel.requestCalendarPermission(context as AppCompatActivity)
},
modifier = Modifier.padding(16.dp)
)
}
PreferenceWithSwitch(
title = stringResource(R.string.preference_search_calendar),
summary = stringResource(R.string.preference_search_calendar_summary),
switchValue = calendar == true,
onSwitchChanged = {
viewModel.setCalendarSearch(it)
},
icon = Icons.Rounded.Today,
enabled = hasCalendarPermission == true,
onClick = {
navController?.navigate("settings/search/calendar/local")
}
)
}
val hasAppShortcutsPermission by viewModel.hasAppShortcutPermission.collectAsStateWithLifecycle( val hasAppShortcutsPermission by viewModel.hasAppShortcutPermission.collectAsStateWithLifecycle(
null null

View File

@ -5,6 +5,7 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import de.mm20.launcher2.permissions.PermissionGroup import de.mm20.launcher2.permissions.PermissionGroup
import de.mm20.launcher2.permissions.PermissionsManager import de.mm20.launcher2.permissions.PermissionsManager
import de.mm20.launcher2.plugins.PluginService
import de.mm20.launcher2.preferences.search.CalculatorSearchSettings import de.mm20.launcher2.preferences.search.CalculatorSearchSettings
import de.mm20.launcher2.preferences.search.CalendarSearchSettings import de.mm20.launcher2.preferences.search.CalendarSearchSettings
import de.mm20.launcher2.preferences.search.ContactSearchSettings import de.mm20.launcher2.preferences.search.ContactSearchSettings
@ -32,6 +33,7 @@ class SearchSettingsScreenVM : ViewModel(), KoinComponent {
private val calculatorSearchSettings: CalculatorSearchSettings by inject() private val calculatorSearchSettings: CalculatorSearchSettings by inject()
private val searchFilterSettings: SearchFilterSettings by inject() private val searchFilterSettings: SearchFilterSettings by inject()
private val pluginService: PluginService by inject()
private val permissionsManager: PermissionsManager by inject() private val permissionsManager: PermissionsManager by inject()
private val locationSearchSettings: LocationSearchSettings by inject() private val locationSearchSettings: LocationSearchSettings by inject()
@ -42,6 +44,14 @@ class SearchSettingsScreenVM : ViewModel(), KoinComponent {
searchUiSettings.setFavorites(favorites) searchUiSettings.setFavorites(favorites)
} }
val hasCalendarPermission = permissionsManager.hasPermission(PermissionGroup.Calendar)
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
val calendarSearch = calendarSearchSettings.isProviderEnabled("local")
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setCalendarSearch(enabled: Boolean) {
calendarSearchSettings.setProviderEnabled("local", enabled)
}
val hasContactsPermission = permissionsManager.hasPermission(PermissionGroup.Contacts) val hasContactsPermission = permissionsManager.hasPermission(PermissionGroup.Contacts)
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
@ -52,6 +62,10 @@ class SearchSettingsScreenVM : ViewModel(), KoinComponent {
contactSearchSettings.setEnabled(contacts) contactSearchSettings.setEnabled(contacts)
} }
fun requestCalendarPermission(activity: AppCompatActivity) {
permissionsManager.requestPermission(activity, PermissionGroup.Calendar)
}
fun requestContactsPermission(activity: AppCompatActivity) { fun requestContactsPermission(activity: AppCompatActivity) {
permissionsManager.requestPermission(activity, PermissionGroup.Contacts) permissionsManager.requestPermission(activity, PermissionGroup.Contacts)
} }
@ -130,4 +144,7 @@ class SearchSettingsScreenVM : ViewModel(), KoinComponent {
fun setSearchFilters(searchFilters: SearchFilters) { fun setSearchFilters(searchFilters: SearchFilters) {
searchFilterSettings.setDefaultFilter(searchFilters) searchFilterSettings.setDefaultFilter(searchFilters)
} }
val plugins = pluginService.getPluginsWithState(enabled = true)
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), emptyList())
} }

View File

@ -608,6 +608,7 @@
<string name="preference_search_contacts_summary">Search contacts on this device</string> <string name="preference_search_contacts_summary">Search contacts on this device</string>
<string name="preference_search_calendar">Calendar</string> <string name="preference_search_calendar">Calendar</string>
<string name="preference_search_calendar_summary">Search upcoming appointments and events</string> <string name="preference_search_calendar_summary">Search upcoming appointments and events</string>
<string name="preference_search_local_calendar_summary">Search calendars on this device</string>
<string name="preference_search_appshortcuts">App shortcuts</string> <string name="preference_search_appshortcuts">App shortcuts</string>
<string name="preference_search_appshortcuts_summary">Search app shortcuts</string> <string name="preference_search_appshortcuts_summary">Search app shortcuts</string>
<string name="preference_search_calculator">Calculator</string> <string name="preference_search_calculator">Calculator</string>

View File

@ -12,6 +12,8 @@ class CalendarSearchSettings internal constructor(
val enabledProviders val enabledProviders
get() = dataStore.data.map { it.calendarSearchProviders } get() = dataStore.data.map { it.calendarSearchProviders }
fun isProviderEnabled(provider: String) = dataStore.data.map { it.calendarSearchProviders.contains(provider) }
fun setProviderEnabled(provider: String, enabled: Boolean) { fun setProviderEnabled(provider: String, enabled: Boolean) {
dataStore.update { dataStore.update {
if (enabled) { if (enabled) {

View File

@ -47,6 +47,9 @@ interface PluginService {
enabled: Boolean? = null, enabled: Boolean? = null,
): Flow<List<PluginWithState>> ): Flow<List<PluginWithState>>
/**
* Get a plugin with its current state or null if the plugin is not found.
*/
fun getPluginWithState( fun getPluginWithState(
authority: String, authority: String,
): Flow<PluginWithState?> ): Flow<PluginWithState?>