Add calendar plugin preferences

This commit is contained in:
MM20 2024-08-06 21:10:54 +02:00
parent 1a97e01c6a
commit 1dad8b48d5
No known key found for this signature in database
GPG Key ID: 0B61A8F2DEAFA389
10 changed files with 318 additions and 25 deletions

View File

@ -1,6 +1,8 @@
package de.mm20.launcher2.ui.component.preferences package de.mm20.launcher2.ui.component.preferences
import androidx.compose.material3.Checkbox import androidx.compose.material3.Checkbox
import androidx.compose.material3.CheckboxColors
import androidx.compose.material3.CheckboxDefaults
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.ImageVector
@ -12,7 +14,8 @@ fun CheckboxPreference(
summary: String? = null, summary: String? = null,
value: Boolean, value: Boolean,
onValueChanged: (Boolean) -> Unit, onValueChanged: (Boolean) -> Unit,
enabled: Boolean = true enabled: Boolean = true,
checkboxColors: CheckboxColors = CheckboxDefaults.colors(),
) { ) {
Preference( Preference(
title = title, title = title,
@ -25,7 +28,7 @@ fun CheckboxPreference(
}, },
controls = { controls = {
Checkbox( Checkbox(
enabled = enabled, checked = value, onCheckedChange = onValueChanged, enabled = enabled, checked = value, onCheckedChange = onValueChanged, colors = checkboxColors
) )
} }
) )

View File

@ -19,7 +19,8 @@ fun PreferenceWithSwitch(
enabled: Boolean = true, enabled: Boolean = true,
onClick: () -> Unit = {}, onClick: () -> Unit = {},
switchValue: Boolean, switchValue: Boolean,
onSwitchChanged: (Boolean) -> Unit onSwitchChanged: (Boolean) -> Unit,
iconPadding: Boolean = true
) { ) {
Row( Row(
verticalAlignment = (Alignment.CenterVertically) verticalAlignment = (Alignment.CenterVertically)
@ -32,7 +33,8 @@ fun PreferenceWithSwitch(
summary = summary, summary = summary,
icon = icon, icon = icon,
enabled = enabled, enabled = enabled,
onClick = onClick onClick = onClick,
iconPadding = iconPadding
) )
} }
Box( Box(

View File

@ -4,26 +4,46 @@ import android.app.PendingIntent
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.layout.wrapContentHeight
import androidx.compose.foundation.layout.wrapContentWidth
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.ErrorOutline import androidx.compose.material.icons.rounded.ErrorOutline
import androidx.compose.material3.AlertDialogDefaults
import androidx.compose.material3.BasicAlertDialog
import androidx.compose.material3.CheckboxDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
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.remember
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.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.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewmodel.compose.viewModel 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.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.PreferenceScreen import de.mm20.launcher2.ui.component.preferences.PreferenceScreen
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.ui.locals.LocalDarkTheme
@Composable @Composable
fun CalendarSearchSettingsScreen() { fun CalendarSearchSettingsScreen() {
@ -34,6 +54,14 @@ fun CalendarSearchSettingsScreen() {
val plugins by viewModel.availablePlugins.collectAsState(emptyList()) val plugins by viewModel.availablePlugins.collectAsState(emptyList())
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) { AnimatedVisibility(hasCalendarPermission == false) {
@ -45,21 +73,32 @@ fun CalendarSearchSettingsScreen() {
modifier = Modifier.padding(16.dp) modifier = Modifier.padding(16.dp)
) )
} }
SwitchPreference( val selectedCalendars = remember(excludedCalendars, calendarLists) {
calendarLists?.count { it.providerId == "local" }
?.minus(excludedCalendars.count {
it.startsWith("local")
})
}
PreferenceWithSwitch(
title = stringResource(R.string.preference_search_calendar), title = stringResource(R.string.preference_search_calendar),
summary = stringResource(R.string.preference_search_calendar_summary), summary = if (selectedCalendars != null && calendarLists != null) "$selectedCalendars lists selected"
value = enabledProviders.contains("local") && hasCalendarPermission == true, else stringResource(R.string.preference_search_calendar_summary),
onValueChanged = { switchValue = enabledProviders.contains("local") && hasCalendarPermission == true,
onSwitchChanged = {
viewModel.setProviderEnabled("local", it) viewModel.setProviderEnabled("local", it)
}, },
enabled = hasCalendarPermission == true enabled = hasCalendarPermission == true,
onClick = {
showDialogForProvider = "local"
}
) )
for (plugin in plugins) { for (plugin in plugins) {
val state = plugin.state val state = plugin.state
if (state is PluginState.SetupRequired) { if (state is PluginState.SetupRequired) {
Banner( Banner(
modifier = Modifier.padding(16.dp), modifier = Modifier.padding(16.dp),
text = state.message ?: stringResource(id = R.string.plugin_state_setup_required), text = state.message
?: stringResource(id = R.string.plugin_state_setup_required),
icon = Icons.Rounded.ErrorOutline, icon = Icons.Rounded.ErrorOutline,
primaryAction = { primaryAction = {
TextButton(onClick = { TextButton(onClick = {
@ -74,18 +113,76 @@ fun CalendarSearchSettingsScreen() {
} }
) )
} }
SwitchPreference( val selectedCalendars = remember(excludedCalendars, calendarLists) {
calendarLists?.count { it.providerId == plugin.plugin.authority }
?.minus(excludedCalendars.count {
it.startsWith(
plugin.plugin.authority
)
})
}
PreferenceWithSwitch(
title = plugin.plugin.label, title = plugin.plugin.label,
enabled = state is PluginState.Ready, enabled = state is PluginState.Ready,
summary = (state as? PluginState.Ready)?.text summary = (state as? PluginState.SetupRequired)?.message
?: (state as? PluginState.SetupRequired)?.message ?: if (selectedCalendars != null && calendarLists != null) "$selectedCalendars lists selected"
?: plugin.plugin.description, else (state as? PluginState.Ready)?.text ?: plugin.plugin.description,
value = enabledProviders.contains(plugin.plugin.authority) && state is PluginState.Ready, switchValue = enabledProviders.contains(plugin.plugin.authority) && state is PluginState.Ready,
onValueChanged = { onSwitchChanged = {
viewModel.setProviderEnabled(plugin.plugin.authority, it) viewModel.setProviderEnabled(plugin.plugin.authority, it)
}, },
onClick = {
showDialogForProvider = plugin.plugin.authority
}
) )
} }
} }
} }
val dialogCalendarLists by remember {
derivedStateOf {
if (showDialogForProvider == null) null
else calendarLists?.filter { it.providerId == showDialogForProvider }
}
}
if (showDialogForProvider != null && dialogCalendarLists != null) {
BasicAlertDialog(
onDismissRequest = {
showDialogForProvider = null
},
) {
Surface(
modifier = Modifier
.wrapContentWidth()
.wrapContentHeight(),
shape = MaterialTheme.shapes.large,
tonalElevation = AlertDialogDefaults.TonalElevation
) {
LazyColumn {
items(dialogCalendarLists ?: emptyList()) {
CheckboxPreference(
title = it.name,
summary = it.owner,
iconPadding = false,
value = it.id !in excludedCalendars,
onValueChanged = { value ->
viewModel.setCalendarExcluded(it.id, !value)
},
checkboxColors = CheckboxDefaults.colors(
checkedColor = if (it.color == 0) MaterialTheme.colorScheme.primary
else Color(
it.color.atTone(if (LocalDarkTheme.current) 80 else 40)
),
checkmarkColor = if (it.color == 0) MaterialTheme.colorScheme.onPrimary
else Color(
it.color.atTone(if (LocalDarkTheme.current) 20 else 100)
)
)
)
}
}
}
}
}
} }

View File

@ -2,16 +2,21 @@ package de.mm20.launcher2.ui.settings.calendarsearch
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import de.mm20.launcher2.calendar.CalendarRepository
import de.mm20.launcher2.calendar.providers.CalendarList
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.plugin.Plugin
import de.mm20.launcher2.plugin.PluginType import de.mm20.launcher2.plugin.PluginType
import de.mm20.launcher2.plugins.PluginService import de.mm20.launcher2.plugins.PluginService
import de.mm20.launcher2.preferences.search.CalendarSearchSettings import de.mm20.launcher2.preferences.search.CalendarSearchSettings
import kotlinx.coroutines.flow.Flow
import org.koin.core.component.KoinComponent import org.koin.core.component.KoinComponent
import org.koin.core.component.inject import org.koin.core.component.inject
class CalendarSearchSettingsScreenVM : ViewModel(), KoinComponent { class CalendarSearchSettingsScreenVM : ViewModel(), KoinComponent {
private val settings: CalendarSearchSettings by inject() private val settings: CalendarSearchSettings by inject()
private val calendarRepository: CalendarRepository by inject()
private val pluginService: PluginService by inject() private val pluginService: PluginService by inject()
private val permissionsManager: PermissionsManager by inject() private val permissionsManager: PermissionsManager by inject()
@ -31,4 +36,11 @@ class CalendarSearchSettingsScreenVM : ViewModel(), KoinComponent {
fun requestCalendarPermission(activity: AppCompatActivity) { fun requestCalendarPermission(activity: AppCompatActivity) {
permissionsManager.requestPermission(activity, PermissionGroup.Calendar) permissionsManager.requestPermission(activity, PermissionGroup.Calendar)
} }
val calendarLists = calendarRepository.getCalendars()
val excludedCalendars = settings.excludedCalendars
fun setCalendarExcluded(calendarId: String, excluded: Boolean) {
settings.setCalendarExcluded(calendarId, excluded)
}
} }

View File

@ -16,6 +16,10 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.rounded.ArrowBack import androidx.compose.material.icons.automirrored.rounded.ArrowBack
@ -29,6 +33,9 @@ import androidx.compose.material.icons.rounded.Place
import androidx.compose.material.icons.rounded.Settings import androidx.compose.material.icons.rounded.Settings
import androidx.compose.material.icons.rounded.Today import androidx.compose.material.icons.rounded.Today
import androidx.compose.material.icons.rounded.Verified import androidx.compose.material.icons.rounded.Verified
import androidx.compose.material3.AlertDialogDefaults
import androidx.compose.material3.BasicAlertDialog
import androidx.compose.material3.CheckboxDefaults
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
@ -39,9 +46,14 @@ import androidx.compose.material3.TextButton
import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
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.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
@ -53,11 +65,15 @@ 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.plugin.PluginType import de.mm20.launcher2.plugin.PluginType
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.preferences.CheckboxPreference
import de.mm20.launcher2.ui.component.preferences.Preference import de.mm20.launcher2.ui.component.preferences.Preference
import de.mm20.launcher2.ui.component.preferences.PreferenceCategory import de.mm20.launcher2.ui.component.preferences.PreferenceCategory
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.ui.locals.LocalDarkTheme
import de.mm20.launcher2.ui.locals.LocalNavController import de.mm20.launcher2.ui.locals.LocalNavController
@Composable @Composable
@ -88,6 +104,11 @@ fun PluginSettingsScreen(pluginId: String) {
minActiveState = Lifecycle.State.RESUMED minActiveState = Lifecycle.State.RESUMED
) )
val calendarPlugins by viewModel.calendarPlugins.collectAsStateWithLifecycle(
emptyList(),
minActiveState = Lifecycle.State.RESUMED
)
val weatherPlugins by viewModel.weatherPlugins.collectAsStateWithLifecycle( val weatherPlugins by viewModel.weatherPlugins.collectAsStateWithLifecycle(
emptyList(), emptyList(),
minActiveState = Lifecycle.State.RESUMED minActiveState = Lifecycle.State.RESUMED
@ -108,6 +129,10 @@ fun PluginSettingsScreen(pluginId: String) {
null null
) )
val enabledCalendarSearchPlugins by viewModel.enabledCalendarSearchPlugins.collectAsStateWithLifecycle(
null
)
val weatherProviderId by viewModel.weatherProvider.collectAsStateWithLifecycle( val weatherProviderId by viewModel.weatherProvider.collectAsStateWithLifecycle(
null null
) )
@ -316,7 +341,9 @@ fun PluginSettingsScreen(pluginId: String) {
primaryAction = { primaryAction = {
TextButton(onClick = { TextButton(onClick = {
try { try {
state.setupActivity.sendWithBackgroundPermission(context) state.setupActivity.sendWithBackgroundPermission(
context
)
} catch (e: PendingIntent.CanceledException) { } catch (e: PendingIntent.CanceledException) {
CrashReporter.logException(e) CrashReporter.logException(e)
} }
@ -367,7 +394,9 @@ fun PluginSettingsScreen(pluginId: String) {
primaryAction = { primaryAction = {
TextButton(onClick = { TextButton(onClick = {
try { try {
state.setupActivity.sendWithBackgroundPermission(context) state.setupActivity.sendWithBackgroundPermission(
context
)
} catch (e: PendingIntent.CanceledException) { } catch (e: PendingIntent.CanceledException) {
CrashReporter.logException(e) CrashReporter.logException(e)
} }
@ -402,6 +431,111 @@ fun PluginSettingsScreen(pluginId: String) {
} }
} }
} }
if (calendarPlugins.isNotEmpty()) {
PreferenceCategory(
stringResource(R.string.plugin_type_calendar),
iconPadding = false,
) {
val excludedCalendars by viewModel.excludedCalendars.collectAsState(
emptySet()
)
for (plugin in calendarPlugins) {
val state = plugin.state
if (state is PluginState.SetupRequired) {
Banner(
modifier = Modifier.padding(16.dp),
text = state.message
?: stringResource(R.string.plugin_state_setup_required),
icon = Icons.Rounded.Info,
primaryAction = {
TextButton(onClick = {
try {
state.setupActivity.sendWithBackgroundPermission(
context
)
} catch (e: PendingIntent.CanceledException) {
CrashReporter.logException(e)
}
}) {
Text(stringResource(R.string.plugin_action_setup))
}
}
)
} else if (state is PluginState.Error) {
Banner(
modifier = Modifier.padding(16.dp),
text = stringResource(R.string.plugin_state_error),
icon = Icons.Rounded.Error,
color = MaterialTheme.colorScheme.errorContainer,
)
}
val calendarLists by remember(plugin.state, plugin.plugin) {
viewModel.getCalendarLists(plugin.plugin)
}.collectAsStateWithLifecycle(null, minActiveState = Lifecycle.State.RESUMED)
var showDialog by remember { mutableStateOf(false) }
val selectedCalendars = remember(excludedCalendars, calendarLists) {
calendarLists?.size?.minus(excludedCalendars.count { it.startsWith(plugin.plugin.authority) })
}
PreferenceWithSwitch(
title = plugin.plugin.label,
enabled = enabledCalendarSearchPlugins != null && state is PluginState.Ready,
summary = (state as? PluginState.SetupRequired)?.message
?: if (selectedCalendars != null && calendarLists != null) "$selectedCalendars lists selected"
else (state as? PluginState.Ready)?.text ?: plugin.plugin.description,
switchValue = enabledCalendarSearchPlugins?.contains(plugin.plugin.authority) == true && state is PluginState.Ready,
onSwitchChanged = {
viewModel.setCalendarSearchPluginEnabled(
plugin.plugin.authority,
it
)
},
iconPadding = false,
onClick = {
showDialog = true
}
)
if (showDialog && calendarLists?.isNotEmpty() == true) {
BasicAlertDialog(
onDismissRequest = {
showDialog = false
},
) {
Surface(
modifier = Modifier.wrapContentWidth()
.wrapContentHeight(),
shape = MaterialTheme.shapes.large,
tonalElevation = AlertDialogDefaults.TonalElevation
) {
LazyColumn {
items(calendarLists!!) {
CheckboxPreference(
title = it.name,
summary = it.owner,
iconPadding = false,
value = it.id !in excludedCalendars,
onValueChanged = { value ->
viewModel.setCalendarExcluded(it.id, !value)
},
checkboxColors = CheckboxDefaults.colors(
checkedColor = if (it.color == 0) MaterialTheme.colorScheme.primary
else Color(
it.color.atTone(if (LocalDarkTheme.current) 80 else 40)
),
checkmarkColor = if (it.color == 0) MaterialTheme.colorScheme.onPrimary
else Color(
it.color.atTone(if (LocalDarkTheme.current) 20 else 100)
)
)
)
}
}
}
}
}
}
}
}
if (weatherPlugins.isNotEmpty()) { if (weatherPlugins.isNotEmpty()) {
PreferenceCategory( PreferenceCategory(
stringResource(R.string.plugin_type_weather), stringResource(R.string.plugin_type_weather),
@ -418,7 +552,9 @@ fun PluginSettingsScreen(pluginId: String) {
primaryAction = { primaryAction = {
TextButton(onClick = { TextButton(onClick = {
try { try {
state.setupActivity.sendWithBackgroundPermission(context) state.setupActivity.sendWithBackgroundPermission(
context
)
} catch (e: PendingIntent.CanceledException) { } catch (e: PendingIntent.CanceledException) {
CrashReporter.logException(e) CrashReporter.logException(e)
} }

View File

@ -7,12 +7,16 @@ import android.net.Uri
import android.provider.Settings import android.provider.Settings
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import de.mm20.launcher2.calendar.CalendarRepository
import de.mm20.launcher2.calendar.providers.CalendarList
import de.mm20.launcher2.ktx.tryStartActivity import de.mm20.launcher2.ktx.tryStartActivity
import de.mm20.launcher2.plugin.Plugin
import de.mm20.launcher2.plugin.PluginPackage import de.mm20.launcher2.plugin.PluginPackage
import de.mm20.launcher2.plugin.PluginState import de.mm20.launcher2.plugin.PluginState
import de.mm20.launcher2.plugin.PluginType import de.mm20.launcher2.plugin.PluginType
import de.mm20.launcher2.plugins.PluginService import de.mm20.launcher2.plugins.PluginService
import de.mm20.launcher2.plugins.PluginWithState import de.mm20.launcher2.plugins.PluginWithState
import de.mm20.launcher2.preferences.search.CalendarSearchSettings
import de.mm20.launcher2.preferences.search.FileSearchSettings import de.mm20.launcher2.preferences.search.FileSearchSettings
import de.mm20.launcher2.preferences.search.LocationSearchSettings import de.mm20.launcher2.preferences.search.LocationSearchSettings
import de.mm20.launcher2.preferences.weather.WeatherSettings import de.mm20.launcher2.preferences.weather.WeatherSettings
@ -31,8 +35,10 @@ import org.koin.core.component.inject
class PluginSettingsScreenVM : ViewModel(), KoinComponent { class PluginSettingsScreenVM : ViewModel(), KoinComponent {
private val pluginService by inject<PluginService>() private val pluginService by inject<PluginService>()
private val calendarRepository by inject<CalendarRepository>()
private val fileSearchSettings: FileSearchSettings by inject() private val fileSearchSettings: FileSearchSettings by inject()
private val locationSearchSettings: LocationSearchSettings by inject() private val locationSearchSettings: LocationSearchSettings by inject()
private val calendarSearchSettings: CalendarSearchSettings by inject()
private val weatherSettings: WeatherSettings by inject() private val weatherSettings: WeatherSettings by inject()
private var pluginPackageName = MutableStateFlow<String?>(null) private var pluginPackageName = MutableStateFlow<String?>(null)
@ -79,6 +85,11 @@ class PluginSettingsScreenVM : ViewModel(), KoinComponent {
it.filter { it.plugin.type == PluginType.LocationSearch } it.filter { it.plugin.type == PluginType.LocationSearch }
} }
val calendarPlugins = states
.map {
it.filter { it.plugin.type == PluginType.Calendar }
}
val weatherPlugins = states val weatherPlugins = states
.map { .map {
it.filter { it.plugin.type == PluginType.Weather } it.filter { it.plugin.type == PluginType.Weather }
@ -121,8 +132,22 @@ class PluginSettingsScreenVM : ViewModel(), KoinComponent {
locationSearchSettings.setPluginEnabled(authority, enabled) locationSearchSettings.setPluginEnabled(authority, enabled)
} }
val enabledCalendarSearchPlugins = calendarSearchSettings.providers
fun setCalendarSearchPluginEnabled(authority: String, enabled: Boolean) {
calendarSearchSettings.setProviderEnabled(authority, enabled)
}
val weatherProvider = weatherSettings.providerId val weatherProvider = weatherSettings.providerId
fun setWeatherProvider(providerId: String) { fun setWeatherProvider(providerId: String) {
weatherSettings.setProvider(providerId) weatherSettings.setProvider(providerId)
} }
fun getCalendarLists(plugin: Plugin): Flow<List<CalendarList>> {
return calendarRepository.getCalendars(plugin.authority)
}
val excludedCalendars = calendarSearchSettings.excludedCalendars
fun setCalendarExcluded(calendarId: String, excluded: Boolean) {
calendarSearchSettings.setCalendarExcluded(calendarId, excluded)
}
} }

View File

@ -56,6 +56,7 @@ data class LauncherSettingsData internal constructor(
@Deprecated("Use calendarSearchProviders `local` instead") @Deprecated("Use calendarSearchProviders `local` instead")
val calendarSearchEnabled: Boolean = true, val calendarSearchEnabled: Boolean = true,
val calendarSearchProviders: Set<String> = setOf("local"), val calendarSearchProviders: Set<String> = setOf("local"),
val calendarSearchExcludedCalendars: Set<String> = setOf(),
val shortcutSearchEnabled: Boolean = true, val shortcutSearchEnabled: Boolean = true,

View File

@ -21,4 +21,16 @@ class CalendarSearchSettings internal constructor(
} }
} }
} }
val excludedCalendars
get() = dataStore.data.map { it.calendarSearchExcludedCalendars }
fun setCalendarExcluded(calendarId: String, excluded: Boolean) {
dataStore.update {
if (excluded) {
it.copy(calendarSearchExcludedCalendars = it.calendarSearchExcludedCalendars + calendarId)
} else {
it.copy(calendarSearchExcludedCalendars = it.calendarSearchExcludedCalendars - calendarId)
}
}
}
} }

View File

@ -1,6 +1,7 @@
package de.mm20.launcher2.calendar package de.mm20.launcher2.calendar
import android.content.Context import android.content.Context
import android.util.Log
import de.mm20.launcher2.calendar.providers.AndroidCalendarProvider import de.mm20.launcher2.calendar.providers.AndroidCalendarProvider
import de.mm20.launcher2.calendar.providers.CalendarList import de.mm20.launcher2.calendar.providers.CalendarList
import de.mm20.launcher2.calendar.providers.CalendarProvider import de.mm20.launcher2.calendar.providers.CalendarProvider
@ -58,8 +59,9 @@ internal class CalendarRepositoryImpl(
val hasPermission = permissionsManager.hasPermission(PermissionGroup.Calendar) val hasPermission = permissionsManager.hasPermission(PermissionGroup.Calendar)
val providerIds = settings.providers val providerIds = settings.providers
val excludedCalendars = settings.excludedCalendars
return combineTransform(hasPermission, providerIds) { perm, providerIds -> return combineTransform(hasPermission, providerIds, excludedCalendars) { perm, providerIds, excludedCalendars ->
val providers = providerIds.mapNotNull { val providers = providerIds.mapNotNull {
when (it) { when (it) {
"local" -> if (perm) AndroidCalendarProvider(context) else null "local" -> if (perm) AndroidCalendarProvider(context) else null
@ -72,6 +74,7 @@ internal class CalendarRepositoryImpl(
queryCalendarEvents( queryCalendarEvents(
query = query, query = query,
excludeAllDayEvents = false, excludeAllDayEvents = false,
excludeCalendars = excludedCalendars.toList(),
providers = providers, providers = providers,
intervalStart = now, intervalStart = now,
intervalEnd = now + 730.days.inWholeMilliseconds, intervalEnd = now + 730.days.inWholeMilliseconds,

View File

@ -47,10 +47,12 @@ class PluginCalendarProvider(
} }
override fun Uri.Builder.appendQueryParameters(query: CalendarQuery): Uri.Builder { override fun Uri.Builder.appendQueryParameters(query: CalendarQuery): Uri.Builder {
if (query.query != null) {
appendQueryParameter( appendQueryParameter(
CalendarPluginContract.Params.Query, CalendarPluginContract.Params.Query,
query.query query.query
) )
}
val start = query.start val start = query.start
if (start != null) { if (start != null) {
appendQueryParameter( appendQueryParameter(