From cc9bff436a166bc633590651f1118c739039a59d Mon Sep 17 00:00:00 2001
From: MM20 <15646950+MM2-0@users.noreply.github.com>
Date: Fri, 16 Aug 2024 23:26:26 +0200
Subject: [PATCH] Restructure calendar widget settings
---
.../launcher/search/calendar/CalendarItem.kt | 17 ++-
.../launcher/sheets/ConfigureWidgetSheet.kt | 134 ++++++++++++------
.../widgets/calendar/CalendarWidget.kt | 6 +-
.../widgets/calendar/CalendarWidgetVM.kt | 5 +-
.../CalendarSearchSettingsScreen.kt | 76 +++++-----
core/i18n/src/main/res/values/strings.xml | 1 +
.../providers/AndroidCalendarProvider.kt | 2 +
.../mm20/launcher2/widgets/CalendarWidget.kt | 3 +
8 files changed, 157 insertions(+), 87 deletions(-)
diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/calendar/CalendarItem.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/calendar/CalendarItem.kt
index 39e58f9f..396b7dda 100644
--- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/calendar/CalendarItem.kt
+++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/calendar/CalendarItem.kt
@@ -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
diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/sheets/ConfigureWidgetSheet.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/sheets/ConfigureWidgetSheet.kt
index 755238da..a67abf28 100644
--- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/sheets/ConfigureWidgetSheet.kt
+++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/sheets/ConfigureWidgetSheet.kt
@@ -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)
+ )
)
- }
- )
+ )
+ }
}
}
}
diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/calendar/CalendarWidget.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/calendar/CalendarWidget.kt
index 819b6b09..e7801197 100644
--- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/calendar/CalendarWidget.kt
+++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/calendar/CalendarWidget.kt
@@ -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)
diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/calendar/CalendarWidgetVM.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/calendar/CalendarWidgetVM.kt
index 7fd3a992..53fb063d 100644
--- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/calendar/CalendarWidgetVM.kt
+++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/calendar/CalendarWidgetVM.kt
@@ -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 }
}
}
diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/calendarsearch/CalendarSearchSettingsScreen.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/calendarsearch/CalendarSearchSettingsScreen.kt
index 48d5b87f..93c8070e 100644
--- a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/calendarsearch/CalendarSearchSettingsScreen.kt
+++ b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/calendarsearch/CalendarSearchSettingsScreen.kt
@@ -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)
+ )
)
)
- )
+ }
}
}
}
diff --git a/core/i18n/src/main/res/values/strings.xml b/core/i18n/src/main/res/values/strings.xml
index 59c2f4ae..474ccac2 100644
--- a/core/i18n/src/main/res/values/strings.xml
+++ b/core/i18n/src/main/res/values/strings.xml
@@ -627,6 +627,7 @@
Sign in to search Google Drive
Calendars
Hide all-day events
+ Hide completed tasks
Build information
More information about this build of this app
Style
diff --git a/data/calendar/src/main/java/de/mm20/launcher2/calendar/providers/AndroidCalendarProvider.kt b/data/calendar/src/main/java/de/mm20/launcher2/calendar/providers/AndroidCalendarProvider.kt
index 26819a44..4342d4c1 100644
--- a/data/calendar/src/main/java/de/mm20/launcher2/calendar/providers/AndroidCalendarProvider.kt
+++ b/data/calendar/src/main/java/de/mm20/launcher2/calendar/providers/AndroidCalendarProvider.kt
@@ -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",
)
)
diff --git a/data/widgets/src/main/java/de/mm20/launcher2/widgets/CalendarWidget.kt b/data/widgets/src/main/java/de/mm20/launcher2/widgets/CalendarWidget.kt
index c94a35c9..31951ba0 100644
--- a/data/widgets/src/main/java/de/mm20/launcher2/widgets/CalendarWidget.kt
+++ b/data/widgets/src/main/java/de/mm20/launcher2/widgets/CalendarWidget.kt
@@ -15,6 +15,9 @@ data class CalendarWidgetConfig(
val legacyExcludedCalendarIds: List? = null,
@SerialName("excludedCalendars")
val excludedCalendarIds: List? = null,
+ val completedTasks: Boolean = true,
+ val upcomingEventsCount: Int = 3,
+ val upcomingTaskCount: Int = 3,
)
data class CalendarWidget(
override val id: UUID,