From cf9d1d1621d63b1c067011c869cdb7e4adbcb5f0 Mon Sep 17 00:00:00 2001 From: Paula Date: Fri, 20 Feb 2026 15:16:07 +0100 Subject: [PATCH 1/2] #15 adapt styling for multi-day events on monthlyWidget to look like one rectangle --- .../helpers/MyWidgetMonthlyProvider.kt | 113 +++++++++++++----- ...ay_monthly_event_background_widget_end.xml | 10 ++ ...monthly_event_background_widget_middle.xml | 5 + ..._monthly_event_background_widget_start.xml | 10 ++ ...ay_monthly_event_view_widget_event_end.xml | 49 ++++++++ ...monthly_event_view_widget_event_middle.xml | 49 ++++++++ ..._monthly_event_view_widget_event_start.xml | 49 ++++++++ 7 files changed, 257 insertions(+), 28 deletions(-) create mode 100644 app/src/main/res/drawable/day_monthly_event_background_widget_end.xml create mode 100644 app/src/main/res/drawable/day_monthly_event_background_widget_middle.xml create mode 100644 app/src/main/res/drawable/day_monthly_event_background_widget_start.xml create mode 100644 app/src/main/res/layout/day_monthly_event_view_widget_event_end.xml create mode 100644 app/src/main/res/layout/day_monthly_event_view_widget_event_middle.xml create mode 100644 app/src/main/res/layout/day_monthly_event_view_widget_event_start.xml diff --git a/app/src/main/kotlin/org/fossify/calendar/helpers/MyWidgetMonthlyProvider.kt b/app/src/main/kotlin/org/fossify/calendar/helpers/MyWidgetMonthlyProvider.kt index aee73402f..147c639eb 100644 --- a/app/src/main/kotlin/org/fossify/calendar/helpers/MyWidgetMonthlyProvider.kt +++ b/app/src/main/kotlin/org/fossify/calendar/helpers/MyWidgetMonthlyProvider.kt @@ -99,7 +99,6 @@ class MyWidgetMonthlyProvider : AppWidgetProvider() { val dimCompletedTasks = context.config.dimCompletedTasks val smallerFontSize = context.getWidgetFontSize() - 3f val res = context.resources - val len = days.size val packageName = context.packageName views.apply { setTextColor(R.id.week_num, textColor) @@ -117,15 +116,52 @@ class MyWidgetMonthlyProvider : AppWidgetProvider() { } } - for (i in 0 until len) { - val day = days[i] + val eventDayIndices = mutableMapOf, MutableList>() + val eventByKey = mutableMapOf, Event>() + for (i in days.indices) { + for (event in days[i].dayEvents) { + val key = Pair(event.id ?: 0L, event.startTS) + eventDayIndices.getOrPut(key) { mutableListOf() }.add(i) + if (key !in eventByKey) eventByKey[key] = event + } + } + + val sortedKeys = eventDayIndices.keys.sortedWith( + compareBy( + { -(eventDayIndices[it]!!.size) }, + { (eventByKey[it]!!.flags and FLAG_ALL_DAY) == 0 }, + { eventByKey[it]!!.startTS }, + { eventByKey[it]!!.endTS }, + { eventDayIndices[it]!!.firstOrNull() ?: 0 }, + { eventByKey[it]!!.title } + ) + ) + + val daySlotIndex = IntArray(days.size) { 0 } + val daySlotLists = Array(days.size) { mutableListOf() } + for (key in sortedKeys) { + val dayIndices = eventDayIndices[key]!! + val event = eventByKey[key]!! + val slot = dayIndices.maxOf { daySlotIndex[it] } + for (dayIdx in dayIndices) { + while (daySlotIndex[dayIdx] < slot) { + daySlotLists[dayIdx].add(null) // invisible spacer + daySlotIndex[dayIdx]++ + } + daySlotLists[dayIdx].add(event) + daySlotIndex[dayIdx]++ + } + } + + val titleShownForKey = mutableSetOf>() + for (i in days.indices) { + val day = days[i] val dayTextColor = if (context.config.highlightWeekends && day.isWeekend) { context.config.highlightWeekendsColor } else { textColor } - val weakTextColor = dayTextColor.adjustAlpha(MEDIUM_ALPHA) val currTextColor = if (day.isThisMonth) dayTextColor else weakTextColor val id = res.getIdentifier("day_$i", "id", packageName) @@ -133,33 +169,54 @@ class MyWidgetMonthlyProvider : AppWidgetProvider() { addDayNumber(context, views, day, currTextColor, id) setupDayOpenIntent(context, views, id, day.code) - day.dayEvents = day.dayEvents.asSequence().sortedWith(compareBy({ it.flags and FLAG_ALL_DAY == 0 }, { it.startTS }, { it.title })) - .toMutableList() as ArrayList - - day.dayEvents.forEach { - val backgroundColor = it.color - var eventTextColor = backgroundColor.getContrastColor() - val shouldDim = (it.isTask() && it.isTaskCompleted() && dimCompletedTasks) - || (dimPastEvents && it.isPastEvent && !it.isTask()) - if (shouldDim) { - eventTextColor = eventTextColor.adjustAlpha(MEDIUM_ALPHA) - } - - val newRemoteView = RemoteViews(packageName, R.layout.day_monthly_event_view_widget).apply { - setText(R.id.day_monthly_event_id, it.title.replace(" ", "\u00A0")) - setTextColor(R.id.day_monthly_event_id, eventTextColor) - setTextSize(R.id.day_monthly_event_id, smallerFontSize - 3f) - setVisibleIf(R.id.day_monthly_task_image, it.isTask()) - applyColorFilter(R.id.day_monthly_task_image, eventTextColor) - setInt(R.id.day_monthly_event_background, "setColorFilter", it.color) - - if (it.shouldStrikeThrough()) { - setInt(R.id.day_monthly_event_id, "setPaintFlags", Paint.ANTI_ALIAS_FLAG or Paint.STRIKE_THRU_TEXT_FLAG) - } else { + for (slotEvent in daySlotLists[i]) { + if (slotEvent == null) { + val spacer = RemoteViews(packageName, R.layout.day_monthly_event_view_widget).apply { + setText(R.id.day_monthly_event_id, " ") + setViewVisibility(R.id.day_monthly_event_background, View.INVISIBLE) + setViewVisibility(R.id.day_monthly_task_image, View.GONE) setInt(R.id.day_monthly_event_id, "setPaintFlags", Paint.ANTI_ALIAS_FLAG) } + views.addView(id, spacer) + } else { + val key = Pair(slotEvent.id ?: 0L, slotEvent.startTS) + val showTitle = titleShownForKey.add(key) || i % 7 == 0 + var eventTextColor = slotEvent.color.getContrastColor() + val shouldDim = (slotEvent.isTask() && slotEvent.isTaskCompleted() && dimCompletedTasks) + || (dimPastEvents && slotEvent.isPastEvent && !slotEvent.isTask()) + if (shouldDim) { + eventTextColor = eventTextColor.adjustAlpha(MEDIUM_ALPHA) + } + val dayIndices = eventDayIndices[key]!! + val prevInRun = i > 0 && i % 7 != 0 && dayIndices.contains(i - 1) + val nextInRun = i < days.size - 1 && (i + 1) % 7 != 0 && dayIndices.contains(i + 1) + val eventLayout = when { + prevInRun && nextInRun -> R.layout.day_monthly_event_view_widget_event_middle + prevInRun -> R.layout.day_monthly_event_view_widget_event_end + nextInRun -> R.layout.day_monthly_event_view_widget_event_start + else -> R.layout.day_monthly_event_view_widget + } + val newRemoteView = RemoteViews(packageName, eventLayout).apply { + setTextColor(R.id.day_monthly_event_id, eventTextColor) + setTextSize(R.id.day_monthly_event_id, smallerFontSize - 3f) + setInt(R.id.day_monthly_event_background, "setColorFilter", slotEvent.color) + if (showTitle) { + setText(R.id.day_monthly_event_id, slotEvent.title.replace(" ", "\u00A0")) + setVisibleIf(R.id.day_monthly_task_image, slotEvent.isTask()) + applyColorFilter(R.id.day_monthly_task_image, eventTextColor) + if (slotEvent.shouldStrikeThrough()) { + setInt(R.id.day_monthly_event_id, "setPaintFlags", Paint.ANTI_ALIAS_FLAG or Paint.STRIKE_THRU_TEXT_FLAG) + } else { + setInt(R.id.day_monthly_event_id, "setPaintFlags", Paint.ANTI_ALIAS_FLAG) + } + } else { + setText(R.id.day_monthly_event_id, "") + setVisibleIf(R.id.day_monthly_task_image, false) + setInt(R.id.day_monthly_event_id, "setPaintFlags", Paint.ANTI_ALIAS_FLAG) + } + } + views.addView(id, newRemoteView) } - views.addView(id, newRemoteView) } } } diff --git a/app/src/main/res/drawable/day_monthly_event_background_widget_end.xml b/app/src/main/res/drawable/day_monthly_event_background_widget_end.xml new file mode 100644 index 000000000..70a87c9c8 --- /dev/null +++ b/app/src/main/res/drawable/day_monthly_event_background_widget_end.xml @@ -0,0 +1,10 @@ + + + + + diff --git a/app/src/main/res/drawable/day_monthly_event_background_widget_middle.xml b/app/src/main/res/drawable/day_monthly_event_background_widget_middle.xml new file mode 100644 index 000000000..9d33d5912 --- /dev/null +++ b/app/src/main/res/drawable/day_monthly_event_background_widget_middle.xml @@ -0,0 +1,5 @@ + + + + diff --git a/app/src/main/res/drawable/day_monthly_event_background_widget_start.xml b/app/src/main/res/drawable/day_monthly_event_background_widget_start.xml new file mode 100644 index 000000000..afaae6bc5 --- /dev/null +++ b/app/src/main/res/drawable/day_monthly_event_background_widget_start.xml @@ -0,0 +1,10 @@ + + + + + diff --git a/app/src/main/res/layout/day_monthly_event_view_widget_event_end.xml b/app/src/main/res/layout/day_monthly_event_view_widget_event_end.xml new file mode 100644 index 000000000..56b46d246 --- /dev/null +++ b/app/src/main/res/layout/day_monthly_event_view_widget_event_end.xml @@ -0,0 +1,49 @@ + + + + + + + + + + diff --git a/app/src/main/res/layout/day_monthly_event_view_widget_event_middle.xml b/app/src/main/res/layout/day_monthly_event_view_widget_event_middle.xml new file mode 100644 index 000000000..477a6dfe4 --- /dev/null +++ b/app/src/main/res/layout/day_monthly_event_view_widget_event_middle.xml @@ -0,0 +1,49 @@ + + + + + + + + + + diff --git a/app/src/main/res/layout/day_monthly_event_view_widget_event_start.xml b/app/src/main/res/layout/day_monthly_event_view_widget_event_start.xml new file mode 100644 index 000000000..c8ebb120f --- /dev/null +++ b/app/src/main/res/layout/day_monthly_event_view_widget_event_start.xml @@ -0,0 +1,49 @@ + + + + + + + + + + From ae554ffa85d7d3a2fba09129ede1407a7736f843 Mon Sep 17 00:00:00 2001 From: Paula Date: Fri, 20 Feb 2026 16:07:35 +0100 Subject: [PATCH 2/2] refactor to avoid exceeding method length --- .../helpers/MyWidgetMonthlyProvider.kt | 58 +++++++++++-------- 1 file changed, 35 insertions(+), 23 deletions(-) diff --git a/app/src/main/kotlin/org/fossify/calendar/helpers/MyWidgetMonthlyProvider.kt b/app/src/main/kotlin/org/fossify/calendar/helpers/MyWidgetMonthlyProvider.kt index 147c639eb..43ac8b528 100644 --- a/app/src/main/kotlin/org/fossify/calendar/helpers/MyWidgetMonthlyProvider.kt +++ b/app/src/main/kotlin/org/fossify/calendar/helpers/MyWidgetMonthlyProvider.kt @@ -116,7 +116,6 @@ class MyWidgetMonthlyProvider : AppWidgetProvider() { } } - val eventDayIndices = mutableMapOf, MutableList>() val eventByKey = mutableMapOf, Event>() for (i in days.indices) { @@ -138,21 +137,7 @@ class MyWidgetMonthlyProvider : AppWidgetProvider() { ) ) - val daySlotIndex = IntArray(days.size) { 0 } - val daySlotLists = Array(days.size) { mutableListOf() } - for (key in sortedKeys) { - val dayIndices = eventDayIndices[key]!! - val event = eventByKey[key]!! - val slot = dayIndices.maxOf { daySlotIndex[it] } - for (dayIdx in dayIndices) { - while (daySlotIndex[dayIdx] < slot) { - daySlotLists[dayIdx].add(null) // invisible spacer - daySlotIndex[dayIdx]++ - } - daySlotLists[dayIdx].add(event) - daySlotIndex[dayIdx]++ - } - } + val daySlotLists = buildDaySlotLists(days.size, sortedKeys, eventDayIndices, eventByKey) val titleShownForKey = mutableSetOf>() for (i in days.indices) { @@ -171,13 +156,7 @@ class MyWidgetMonthlyProvider : AppWidgetProvider() { for (slotEvent in daySlotLists[i]) { if (slotEvent == null) { - val spacer = RemoteViews(packageName, R.layout.day_monthly_event_view_widget).apply { - setText(R.id.day_monthly_event_id, " ") - setViewVisibility(R.id.day_monthly_event_background, View.INVISIBLE) - setViewVisibility(R.id.day_monthly_task_image, View.GONE) - setInt(R.id.day_monthly_event_id, "setPaintFlags", Paint.ANTI_ALIAS_FLAG) - } - views.addView(id, spacer) + views.addView(id, createSpacerView(packageName)) } else { val key = Pair(slotEvent.id ?: 0L, slotEvent.startTS) val showTitle = titleShownForKey.add(key) || i % 7 == 0 @@ -221,6 +200,39 @@ class MyWidgetMonthlyProvider : AppWidgetProvider() { } } + private fun buildDaySlotLists( + daysSize: Int, + sortedKeys: List>, + eventDayIndices: Map, List>, + eventByKey: Map, Event> + ): Array> { + val daySlotIndex = IntArray(daysSize) { 0 } + val daySlotLists = Array(daysSize) { mutableListOf() } + for (key in sortedKeys) { + val dayIndices = eventDayIndices[key]!! + val event = eventByKey[key]!! + val slot = dayIndices.maxOf { daySlotIndex[it] } + for (dayIdx in dayIndices) { + while (daySlotIndex[dayIdx] < slot) { + daySlotLists[dayIdx].add(null) // invisible spacer + daySlotIndex[dayIdx]++ + } + daySlotLists[dayIdx].add(event) + daySlotIndex[dayIdx]++ + } + } + return daySlotLists + } + + private fun createSpacerView(packageName: String): RemoteViews { + return RemoteViews(packageName, R.layout.day_monthly_event_view_widget).apply { + setText(R.id.day_monthly_event_id, " ") + setViewVisibility(R.id.day_monthly_event_background, View.INVISIBLE) + setViewVisibility(R.id.day_monthly_task_image, View.GONE) + setInt(R.id.day_monthly_event_id, "setPaintFlags", Paint.ANTI_ALIAS_FLAG) + } + } + private fun addDayNumber(context: Context, views: RemoteViews, day: DayMonthly, textColor: Int, id: Int) { val newRemoteView = RemoteViews(context.packageName, R.layout.day_monthly_number_view).apply { setText(R.id.day_monthly_number_id, day.value.toString())