Add calendar plugin preferences
This commit is contained in:
parent
1a97e01c6a
commit
1dad8b48d5
@ -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
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
@ -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(
|
||||||
|
|||||||
@ -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)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -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,
|
||||||
|
|
||||||
|
|||||||
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -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,
|
||||||
|
|||||||
@ -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(
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user