Restructure calendar widget settings
This commit is contained in:
parent
bf29ce6f08
commit
cc9bff436a
@ -44,6 +44,7 @@ import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.toArgb
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextDecoration
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.roundToIntRect
|
||||
@ -121,7 +122,13 @@ fun CalendarItem(
|
||||
this@AnimatedContent
|
||||
),
|
||||
text = calendar.labelOverride ?: calendar.label,
|
||||
style = MaterialTheme.typography.titleMedium
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
maxLines = 1,
|
||||
textDecoration = if (calendar.isCompleted == true) {
|
||||
TextDecoration.LineThrough
|
||||
} else {
|
||||
TextDecoration.None
|
||||
}
|
||||
)
|
||||
if (calendar.calendarName != null) {
|
||||
Text(
|
||||
@ -300,7 +307,13 @@ fun CalendarItem(
|
||||
this@AnimatedContent
|
||||
),
|
||||
text = calendar.labelOverride ?: calendar.label,
|
||||
style = MaterialTheme.typography.titleSmall
|
||||
style = MaterialTheme.typography.titleSmall,
|
||||
maxLines = 1,
|
||||
textDecoration = if (calendar.isCompleted == true) {
|
||||
TextDecoration.LineThrough
|
||||
} else {
|
||||
TextDecoration.None
|
||||
}
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier
|
||||
|
||||
@ -12,6 +12,7 @@ import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.browser.customtabs.CustomTabColorSchemeParams
|
||||
import androidx.browser.customtabs.CustomTabsIntent
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.gestures.awaitEachGesture
|
||||
import androidx.compose.foundation.gestures.awaitFirstDown
|
||||
@ -34,6 +35,7 @@ import androidx.compose.material.icons.rounded.Link
|
||||
import androidx.compose.material.icons.rounded.LinkOff
|
||||
import androidx.compose.material.icons.rounded.OpenInNew
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.CheckboxDefaults
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
@ -42,7 +44,6 @@ import androidx.compose.material3.OutlinedCard
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
@ -52,22 +53,26 @@ import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.toArgb
|
||||
import androidx.compose.ui.input.pointer.PointerEventPass
|
||||
import androidx.compose.ui.input.pointer.pointerInput
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalLifecycleOwner
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.isUnspecified
|
||||
import androidx.core.net.toUri
|
||||
import androidx.lifecycle.compose.LocalLifecycleOwner
|
||||
import de.mm20.launcher2.calendar.CalendarRepository
|
||||
import de.mm20.launcher2.calendar.providers.CalendarList
|
||||
import de.mm20.launcher2.crashreporter.CrashReporter
|
||||
import de.mm20.launcher2.ktx.isAtLeastApiLevel
|
||||
import de.mm20.launcher2.permissions.PermissionGroup
|
||||
import de.mm20.launcher2.permissions.PermissionsManager
|
||||
import de.mm20.launcher2.plugin.PluginRepository
|
||||
import de.mm20.launcher2.plugin.PluginType
|
||||
import de.mm20.launcher2.search.calendar.CalendarListType
|
||||
import de.mm20.launcher2.themes.atTone
|
||||
import de.mm20.launcher2.ui.R
|
||||
import de.mm20.launcher2.ui.base.LocalAppWidgetHost
|
||||
import de.mm20.launcher2.ui.component.BottomSheetDialog
|
||||
@ -383,7 +388,7 @@ fun ColumnScope.ConfigureAppWidget(
|
||||
val minHeight = if (widgetInfo.minResizeHeight in 1..widgetInfo.minHeight) {
|
||||
widgetInfo.minResizeHeight.toDp()
|
||||
} else {
|
||||
widgetInfo.minHeight.toDp()
|
||||
widgetInfo.minHeight.toDp()
|
||||
}
|
||||
|
||||
DragResizeHandle(
|
||||
@ -503,66 +508,103 @@ fun ColumnScope.ConfigureCalendarWidget(
|
||||
) {
|
||||
val calendarRepository: CalendarRepository = get()
|
||||
val permissionsManager: PermissionsManager = get()
|
||||
val pluginRepository: PluginRepository = get()
|
||||
val calendars by remember {
|
||||
calendarRepository.getCalendars().map {
|
||||
it.sortedBy { it.name }
|
||||
}
|
||||
}.collectAsState(null)
|
||||
val plugins by remember {
|
||||
pluginRepository.findMany(
|
||||
type = PluginType.Calendar,
|
||||
enabled = true,
|
||||
)
|
||||
}.collectAsState(emptyList())
|
||||
|
||||
val hasPermission by remember {
|
||||
permissionsManager.hasPermission(PermissionGroup.Calendar)
|
||||
}.collectAsState(true)
|
||||
|
||||
OutlinedCard {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.preference_calendar_hide_allday),
|
||||
iconPadding = false,
|
||||
value = !widget.config.allDayEvents,
|
||||
onValueChanged = {
|
||||
onWidgetUpdated(widget.copy(config = widget.config.copy(allDayEvents = !it)))
|
||||
}
|
||||
)
|
||||
}
|
||||
val hasTasks = remember(calendars) {
|
||||
calendars?.any { it.types.contains(CalendarListType.Tasks) } == true
|
||||
}
|
||||
Text(
|
||||
modifier = Modifier.padding(top = 16.dp, bottom = 4.dp),
|
||||
style = MaterialTheme.typography.titleSmall,
|
||||
color = MaterialTheme.colorScheme.secondary,
|
||||
text = stringResource(R.string.preference_calendar_calendars)
|
||||
)
|
||||
val context = LocalLifecycleOwner.current as AppCompatActivity
|
||||
val excludedCalendars = remember(widget.config) {
|
||||
widget.config.excludedCalendarIds ?: widget.config.legacyExcludedCalendarIds?.map { "local:$it" } ?: emptyList()
|
||||
}
|
||||
if (calendars?.isNotEmpty() == true) {
|
||||
|
||||
AnimatedVisibility(hasTasks) {
|
||||
OutlinedCard {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
for ((i, calendar) in calendars!!.withIndex()) {
|
||||
if (i > 0) HorizontalDivider()
|
||||
CheckboxPreference(
|
||||
title = calendar.name,
|
||||
summary = calendar.owner,
|
||||
iconPadding = false,
|
||||
value = excludedCalendars.contains(calendar.id) != true,
|
||||
onValueChanged = {
|
||||
onWidgetUpdated(
|
||||
widget.copy(
|
||||
config = widget.config.copy(
|
||||
excludedCalendarIds = if (it) {
|
||||
excludedCalendars - calendar.id
|
||||
} else {
|
||||
excludedCalendars + calendar.id
|
||||
}
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.preference_calendar_hide_completed),
|
||||
iconPadding = false,
|
||||
value = !widget.config.completedTasks,
|
||||
onValueChanged = {
|
||||
onWidgetUpdated(widget.copy(config = widget.config.copy(completedTasks = !it)))
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
val context = LocalLifecycleOwner.current as AppCompatActivity
|
||||
val excludedCalendars = remember(widget.config) {
|
||||
widget.config.excludedCalendarIds
|
||||
?: widget.config.legacyExcludedCalendarIds?.map { "local:$it" } ?: emptyList()
|
||||
}
|
||||
|
||||
val groups = remember(calendars) {
|
||||
calendars?.groupBy { it.providerId }?.entries
|
||||
}
|
||||
|
||||
if (groups?.isNotEmpty() == true) {
|
||||
for (group in groups) {
|
||||
val pluginName = remember(plugins, group.key) {
|
||||
if (group.key == "local") context.getString(R.string.preference_calendar_calendars)
|
||||
else plugins.find { it.authority == group.key }?.label
|
||||
}
|
||||
if (pluginName != null) {
|
||||
Text(
|
||||
modifier = Modifier.padding(top = 16.dp, bottom = 4.dp),
|
||||
style = MaterialTheme.typography.titleSmall,
|
||||
color = MaterialTheme.colorScheme.secondary,
|
||||
text = pluginName
|
||||
)
|
||||
}
|
||||
OutlinedCard {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
for ((i, calendar) in group.value.withIndex()) {
|
||||
if (i > 0) HorizontalDivider()
|
||||
CheckboxPreference(
|
||||
title = calendar.name,
|
||||
summary = calendar.owner,
|
||||
iconPadding = false,
|
||||
value = !excludedCalendars.contains(calendar.id),
|
||||
onValueChanged = {
|
||||
onWidgetUpdated(
|
||||
widget.copy(
|
||||
config = widget.config.copy(
|
||||
excludedCalendarIds = if (it) {
|
||||
excludedCalendars - calendar.id
|
||||
} else {
|
||||
excludedCalendars + calendar.id
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
},
|
||||
checkboxColors = CheckboxDefaults.colors(
|
||||
checkedColor = if (calendar.color == 0) MaterialTheme.colorScheme.primary
|
||||
else Color(
|
||||
calendar.color.atTone(if (LocalDarkTheme.current) 80 else 40)
|
||||
),
|
||||
checkmarkColor = if (calendar.color == 0) MaterialTheme.colorScheme.onPrimary
|
||||
else Color(
|
||||
calendar.color.atTone(if (LocalDarkTheme.current) 20 else 100)
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -208,7 +208,8 @@ fun CalendarWidget(
|
||||
),
|
||||
onClick = {
|
||||
viewModel.showAllEvents()
|
||||
}
|
||||
},
|
||||
modifier = Modifier.padding(top = 8.dp)
|
||||
)
|
||||
}
|
||||
if (nextEvents.isNotEmpty()) {
|
||||
@ -263,10 +264,11 @@ fun CalendarWidget(
|
||||
@Composable
|
||||
private fun Info(
|
||||
text: String,
|
||||
modifier: Modifier = Modifier,
|
||||
onClick: (() -> Unit)? = null,
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.clip(MaterialTheme.shapes.small)
|
||||
.border(1.dp, MaterialTheme.colorScheme.outlineVariant, MaterialTheme.shapes.small)
|
||||
|
||||
@ -176,7 +176,10 @@ class CalendarWidgetVM : ViewModel(), KoinComponent {
|
||||
maxVisibility = VisibilityLevel.SearchOnly,
|
||||
limit = 9999,
|
||||
).collectLatest { hidden ->
|
||||
upcomingEvents = events.filter { !hidden.contains(it.key) }.sortedBy { it.startTime ?: it.endTime }
|
||||
upcomingEvents = events
|
||||
.filter {
|
||||
!hidden.contains(it.key) && !(!config.completedTasks && it.isCompleted == true)
|
||||
}.sortedBy { it.startTime ?: it.endTime }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,20 +1,19 @@
|
||||
package de.mm20.launcher2.ui.settings.calendarsearch
|
||||
|
||||
import android.app.PendingIntent
|
||||
import android.util.Log
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
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.rounded.CalendarToday
|
||||
import androidx.compose.material.icons.rounded.Checklist
|
||||
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.ModalBottomSheet
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
@ -35,14 +34,15 @@ import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import de.mm20.launcher2.crashreporter.CrashReporter
|
||||
import de.mm20.launcher2.ktx.sendWithBackgroundPermission
|
||||
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.component.Banner
|
||||
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.PreferenceScreen
|
||||
import de.mm20.launcher2.ui.component.preferences.PreferenceWithSwitch
|
||||
import de.mm20.launcher2.ui.component.preferences.SwitchPreference
|
||||
import de.mm20.launcher2.ui.locals.LocalDarkTheme
|
||||
|
||||
@Composable
|
||||
@ -76,7 +76,7 @@ fun CalendarSearchSettingsScreen() {
|
||||
val selectedCalendars = remember(excludedCalendars, calendarLists) {
|
||||
calendarLists?.count { it.providerId == "local" }
|
||||
?.minus(excludedCalendars.count {
|
||||
it.startsWith("local")
|
||||
it.startsWith("local:")
|
||||
})
|
||||
}
|
||||
PreferenceWithSwitch(
|
||||
@ -117,7 +117,7 @@ fun CalendarSearchSettingsScreen() {
|
||||
calendarLists?.count { it.providerId == plugin.plugin.authority }
|
||||
?.minus(excludedCalendars.count {
|
||||
it.startsWith(
|
||||
plugin.plugin.authority
|
||||
"${plugin.plugin.authority}:"
|
||||
)
|
||||
})
|
||||
}
|
||||
@ -126,7 +126,7 @@ fun CalendarSearchSettingsScreen() {
|
||||
enabled = 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,
|
||||
else (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)
|
||||
@ -139,6 +139,8 @@ fun CalendarSearchSettingsScreen() {
|
||||
}
|
||||
}
|
||||
|
||||
Log.d("MM20", "${calendarLists.toString()}")
|
||||
|
||||
val dialogCalendarLists by remember {
|
||||
derivedStateOf {
|
||||
if (showDialogForProvider == null) null
|
||||
@ -147,39 +149,41 @@ fun CalendarSearchSettingsScreen() {
|
||||
}
|
||||
|
||||
if (showDialogForProvider != null && dialogCalendarLists != null) {
|
||||
BasicAlertDialog(
|
||||
ModalBottomSheet(
|
||||
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)
|
||||
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)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -627,6 +627,7 @@
|
||||
<string name="preference_google_signin_summary">Sign in to search Google Drive</string>
|
||||
<string name="preference_calendar_calendars">Calendars</string>
|
||||
<string name="preference_calendar_hide_allday">Hide all-day events</string>
|
||||
<string name="preference_calendar_hide_completed">Hide completed tasks</string>
|
||||
<string name="preference_screen_buildinfo">Build information</string>
|
||||
<string name="preference_screen_buildinfo_summary">More information about this build of this app</string>
|
||||
<string name="preference_search_bar_style">Style</string>
|
||||
|
||||
@ -6,6 +6,7 @@ import android.provider.CalendarContract
|
||||
import androidx.core.database.getStringOrNull
|
||||
import de.mm20.launcher2.permissions.PermissionGroup
|
||||
import de.mm20.launcher2.search.CalendarEvent
|
||||
import de.mm20.launcher2.search.calendar.CalendarListType
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.util.Calendar
|
||||
@ -201,6 +202,7 @@ class AndroidCalendarProvider(
|
||||
name = cursor.getStringOrNull(5) ?: cursor.getStringOrNull(1) ?: "",
|
||||
owner = cursor.getStringOrNull(2),
|
||||
color = cursor.getInt(3),
|
||||
types = listOf(CalendarListType.Calendar),
|
||||
providerId = "local",
|
||||
)
|
||||
)
|
||||
|
||||
@ -15,6 +15,9 @@ data class CalendarWidgetConfig(
|
||||
val legacyExcludedCalendarIds: List<Long>? = null,
|
||||
@SerialName("excludedCalendars")
|
||||
val excludedCalendarIds: List<String>? = null,
|
||||
val completedTasks: Boolean = true,
|
||||
val upcomingEventsCount: Int = 3,
|
||||
val upcomingTaskCount: Int = 3,
|
||||
)
|
||||
data class CalendarWidget(
|
||||
override val id: UUID,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user