From 3f81bb9ea508861312fcc4f75fcbe1afaa41fc14 Mon Sep 17 00:00:00 2001 From: Jean-Luc Makiola Date: Mon, 26 Oct 2026 20:08:18 +0100 Subject: [PATCH] Widget picker: generated previews on Android 15+, XML mock-ups redrawn to match the widgets --- .../jeanlucmakiola/calendula/CalendulaApp.kt | 3 + .../calendula/widget/WidgetPickerPreview.kt | 177 ++++ .../calendula/widget/agenda/AgendaWidget.kt | 8 + .../widget/countdown/CountdownWidget.kt | 8 + .../calendula/widget/date/DateWidget.kt | 8 + .../calendula/widget/month/MonthWidget.kt | 17 +- .../widget/quickadd/QuickAddWidget.kt | 8 + .../calendula/widget/split/MonthDayWidget.kt | 8 + .../calendula/widget/today/TodayWidget.kt | 11 +- .../widget/upcoming/UpcomingWidget.kt | 8 + .../calendula/widget/week/WeekStripWidget.kt | 8 + app/src/main/res/drawable/preview_stripe.xml | 2 +- .../res/drawable/preview_widget_cell_bg.xml | 6 + ...row_bg.xml => preview_widget_event_bg.xml} | 4 +- .../drawable/preview_widget_outside_bg.xml | 6 + .../main/res/layout/widget_preview_agenda.xml | 236 +++-- .../res/layout/widget_preview_countdown.xml | 54 +- .../main/res/layout/widget_preview_date.xml | 9 +- .../main/res/layout/widget_preview_month.xml | 830 ++++++++++++++++-- .../res/layout/widget_preview_month_day.xml | 510 ++++++++++- .../res/layout/widget_preview_quick_add.xml | 3 +- .../main/res/layout/widget_preview_today.xml | 63 +- .../res/layout/widget_preview_upcoming.xml | 90 +- .../res/layout/widget_preview_week_strip.xml | 202 ++++- .../widget_preview_colors.xml | 5 +- .../values-night/widget_preview_colors.xml | 5 +- .../res/values-v31/widget_preview_colors.xml | 5 +- app/src/main/res/values/strings.xml | 3 + .../main/res/values/widget_preview_colors.xml | 19 +- .../main/res/values/widget_preview_styles.xml | 26 +- floret-kit | 2 +- 31 files changed, 2071 insertions(+), 273 deletions(-) create mode 100644 app/src/main/java/de/jeanlucmakiola/calendula/widget/WidgetPickerPreview.kt create mode 100644 app/src/main/res/drawable/preview_widget_cell_bg.xml rename app/src/main/res/drawable/{preview_widget_row_bg.xml => preview_widget_event_bg.xml} (60%) create mode 100644 app/src/main/res/drawable/preview_widget_outside_bg.xml diff --git a/app/src/main/java/de/jeanlucmakiola/calendula/CalendulaApp.kt b/app/src/main/java/de/jeanlucmakiola/calendula/CalendulaApp.kt index ab7e1d7..d7cc846 100644 --- a/app/src/main/java/de/jeanlucmakiola/calendula/CalendulaApp.kt +++ b/app/src/main/java/de/jeanlucmakiola/calendula/CalendulaApp.kt @@ -10,6 +10,7 @@ import de.jeanlucmakiola.calendula.data.contacts.SpecialDatesScheduler import de.jeanlucmakiola.calendula.data.contacts.SpecialDatesSyncWorker import de.jeanlucmakiola.calendula.data.reminders.ReminderMaintenanceScheduler import de.jeanlucmakiola.calendula.data.reminders.ReminderMaintenanceWorker +import de.jeanlucmakiola.calendula.widget.publishCalendulaWidgetPreviews import de.jeanlucmakiola.calendula.widget.syncWidgetRollover import de.jeanlucmakiola.calendula.widget.widgetEntryPoint import de.jeanlucmakiola.floret.crash.CrashConfig @@ -58,10 +59,12 @@ class CalendulaApp : Application() { * (#228). Idempotent, and it covers what no broadcast reaches — an alarm * dropped by a force-stop is armed again the next time the app is opened. * Off the main thread: a handful of binder calls on every process start. + * The picker previews are republished here too, once per update. */ private fun reconcileWidgetRollover() { CoroutineScope(SupervisorJob() + Dispatchers.Default).launch { syncWidgetRollover(this@CalendulaApp) + publishCalendulaWidgetPreviews(this@CalendulaApp) } } diff --git a/app/src/main/java/de/jeanlucmakiola/calendula/widget/WidgetPickerPreview.kt b/app/src/main/java/de/jeanlucmakiola/calendula/widget/WidgetPickerPreview.kt new file mode 100644 index 0000000..b2a4788 --- /dev/null +++ b/app/src/main/java/de/jeanlucmakiola/calendula/widget/WidgetPickerPreview.kt @@ -0,0 +1,177 @@ +package de.jeanlucmakiola.calendula.widget + +import android.content.Context +import androidx.glance.appwidget.GlanceAppWidget +import androidx.glance.appwidget.SizeMode +import androidx.glance.appwidget.provideContent +import de.jeanlucmakiola.calendula.R +import de.jeanlucmakiola.calendula.data.prefs.is24Hour +import de.jeanlucmakiola.calendula.data.prefs.resolveFirstDay +import de.jeanlucmakiola.calendula.domain.EventInstance +import de.jeanlucmakiola.calendula.ui.agenda.groupAgendaDays +import de.jeanlucmakiola.calendula.widget.agenda.AgendaWidgetContent +import de.jeanlucmakiola.calendula.widget.agenda.AgendaWidgetReceiver +import de.jeanlucmakiola.calendula.widget.countdown.CountdownData +import de.jeanlucmakiola.calendula.widget.countdown.CountdownOptions +import de.jeanlucmakiola.calendula.widget.countdown.CountdownWidgetContent +import de.jeanlucmakiola.calendula.widget.countdown.CountdownWidgetReceiver +import de.jeanlucmakiola.calendula.widget.date.DateWidgetContent +import de.jeanlucmakiola.calendula.widget.date.DateWidgetReceiver +import de.jeanlucmakiola.calendula.widget.month.MonthWidgetContent +import de.jeanlucmakiola.calendula.widget.month.MonthWidgetReceiver +import de.jeanlucmakiola.calendula.widget.quickadd.QuickAddWidgetContent +import de.jeanlucmakiola.calendula.widget.quickadd.QuickAddWidgetReceiver +import de.jeanlucmakiola.calendula.widget.split.MonthDayData +import de.jeanlucmakiola.calendula.widget.split.MonthDayWidgetContent +import de.jeanlucmakiola.calendula.widget.split.MonthDayWidgetReceiver +import de.jeanlucmakiola.calendula.widget.today.TodayWidgetContent +import de.jeanlucmakiola.calendula.widget.today.TodayWidgetReceiver +import de.jeanlucmakiola.calendula.widget.upcoming.UpcomingWidgetContent +import de.jeanlucmakiola.calendula.widget.upcoming.UpcomingWidgetReceiver +import de.jeanlucmakiola.calendula.widget.week.WeekStripWidgetContent +import de.jeanlucmakiola.calendula.widget.week.WeekStripWidgetReceiver +import de.jeanlucmakiola.floret.glance.publishWidgetPreviews +import kotlinx.coroutines.flow.first +import kotlinx.datetime.DateTimeUnit +import kotlinx.datetime.LocalDate +import kotlinx.datetime.LocalTime +import kotlinx.datetime.TimeZone +import kotlinx.datetime.atStartOfDayIn +import kotlinx.datetime.atTime +import kotlinx.datetime.plus +import kotlinx.datetime.toInstant +import java.util.Locale +import kotlin.time.Duration.Companion.hours +import kotlin.time.Duration.Companion.minutes + +/** Republishes the generated picker previews (Android 15+) after an app update or a language change. */ +internal suspend fun publishCalendulaWidgetPreviews(context: Context) { + publishWidgetPreviews( + context, + version = "${context.packageManager.getPackageInfo(context.packageName, 0).lastUpdateTime}-${Locale.getDefault().toLanguageTag()}", + receivers = listOf( + AgendaWidgetReceiver::class, + MonthWidgetReceiver::class, + QuickAddWidgetReceiver::class, + DateWidgetReceiver::class, + WeekStripWidgetReceiver::class, + UpcomingWidgetReceiver::class, + TodayWidgetReceiver::class, + CountdownWidgetReceiver::class, + MonthDayWidgetReceiver::class, + ), + ) +} + +/** The size [kind]'s picker preview is laid out at. */ +internal fun previewSizeOf(kind: WidgetKind) = SizeMode.Responsive(setOf(kind.defaultSize)) + +/** [kind]'s picker preview: the real widget, drawn with sample events around today's morning. */ +internal suspend fun GlanceAppWidget.provideSamplePreview(context: Context, kind: WidgetKind) { + val settings = context.widgetEntryPoint().settingsPrefs().widgetSettings.first() + val zone = systemZone() + val today = today(zone) + val now = today.atTime(8, 0).toInstant(zone) + val instances = context.sampleInstances(today, zone) + val weekStart = settings.weekStart.resolveFirstDay(Locale.getDefault()) + val is24Hour = settings.timeFormat.is24Hour(android.text.format.DateFormat.is24HourFormat(context)) + val options = settings.agendaDefaults + val appearance = WidgetAppearance() + val index = monthIndexOf(today) + val month = MonthWidgetSource.Ready(today, weekStart, settings.soften, instances, index..index) + fun days(range: ClosedRange) = DaysWidgetData.Ready( + today = today, + firstDay = range.start, + lastDay = range.endInclusive, + days = groupAgendaDays(range.start, range.endInclusive, instances, zone), + weekStart = weekStart, + is24Hour = is24Hour, + soften = settings.soften, + options = options, + now = now, + ) + val theme = settings.themeMode + provideContent { + when (kind) { + WidgetKind.AGENDA -> AgendaWidgetContent( + AgendaWidgetData.Ready( + today = today, + days = groupAgendaDays(today, today.plus(6, DateTimeUnit.DAY), instances, zone), + is24Hour = is24Hour, + soften = settings.soften, + pastDisplay = options.pastDisplay, + showToday = options.showToday, + textSize = options.textSize, + now = now, + ), + appearance, + theme, + preview = true, + ) + WidgetKind.MONTH -> MonthWidgetContent(month, index, appearance, theme) + WidgetKind.MONTH_DAY -> MonthDayWidgetContent( + MonthDayData.Ready(month, options, is24Hour, now), + index, + selected = null, + appearance = appearance, + appTheme = theme, + preview = true, + ) + WidgetKind.QUICK_ADD -> QuickAddWidgetContent(today, appearance, theme) + WidgetKind.DATE -> DateWidgetContent(today, appearance, theme) + WidgetKind.WEEK_STRIP -> WeekStripWidgetContent(days(weekOf(today, weekStart)), appearance, theme) + WidgetKind.UPCOMING -> UpcomingWidgetContent(days(today..today.plus(6, DateTimeUnit.DAY)), appearance, theme) + WidgetKind.TODAY -> TodayWidgetContent(days(today..today.plus(1, DateTimeUnit.DAY)), appearance, theme, preview = true) + WidgetKind.COUNTDOWN -> CountdownWidgetContent( + CountdownData.Ready( + options = CountdownOptions(), + event = instances.first { !it.isAllDay && it.start > now }, + now = now, + today = today, + is24Hour = is24Hour, + soften = settings.soften, + appWidgetId = null, + ), + appearance, + theme, + ) + } + } +} + +/** A plausible few weeks: a busy today, a trip tomorrow, and a scatter of days with something on. */ +private fun Context.sampleInstances(today: LocalDate, zone: TimeZone): List { + var id = 0L + fun timed(day: Int, at: LocalTime, length: kotlin.time.Duration, title: Int, color: Int): EventInstance { + val start = today.plus(day, DateTimeUnit.DAY).atTime(at).toInstant(zone) + id++ + return EventInstance(id, id, 1, getString(title), start, start + length, false, color, null) + } + fun allDay(day: Int, title: Int, color: Int): EventInstance { + val date = today.plus(day, DateTimeUnit.DAY) + id++ + return EventInstance( + id, id, 1, getString(title), + date.atStartOfDayIn(TimeZone.UTC), date.plus(1, DateTimeUnit.DAY).atStartOfDayIn(TimeZone.UTC), + true, color, null, + ) + } + return listOf( + timed(0, LocalTime(9, 30), 30.minutes, R.string.settings_past_events_sample_morning, Blue), + timed(0, LocalTime(12, 30), 1.hours, R.string.settings_past_events_sample_midday, Green), + timed(0, LocalTime(19, 0), 2.hours, R.string.settings_past_events_sample_evening, Purple), + allDay(1, R.string.widget_preview_sample_trip, Orange), + timed(2, LocalTime(10, 0), 1.hours, R.string.widget_preview_sample_dentist, Red), + timed(-6, LocalTime(9, 30), 30.minutes, R.string.settings_past_events_sample_morning, Blue), + timed(-2, LocalTime(19, 0), 2.hours, R.string.settings_past_events_sample_evening, Purple), + timed(5, LocalTime(19, 0), 2.hours, R.string.settings_past_events_sample_evening, Purple), + timed(8, LocalTime(9, 30), 30.minutes, R.string.settings_past_events_sample_morning, Blue), + timed(12, LocalTime(12, 30), 1.hours, R.string.settings_past_events_sample_midday, Green), + ).sortedBy { it.start } +} + +private const val Blue = 0xFF039BE5.toInt() +private const val Green = 0xFF33B679.toInt() +private const val Purple = 0xFF8E24AA.toInt() +private const val Orange = 0xFFF4511E.toInt() +private const val Red = 0xFFD50000.toInt() diff --git a/app/src/main/java/de/jeanlucmakiola/calendula/widget/agenda/AgendaWidget.kt b/app/src/main/java/de/jeanlucmakiola/calendula/widget/agenda/AgendaWidget.kt index 554265c..7afaf48 100644 --- a/app/src/main/java/de/jeanlucmakiola/calendula/widget/agenda/AgendaWidget.kt +++ b/app/src/main/java/de/jeanlucmakiola/calendula/widget/agenda/AgendaWidget.kt @@ -1,5 +1,8 @@ package de.jeanlucmakiola.calendula.widget.agenda +import de.jeanlucmakiola.calendula.widget.WidgetKind +import de.jeanlucmakiola.calendula.widget.previewSizeOf +import de.jeanlucmakiola.calendula.widget.provideSamplePreview import de.jeanlucmakiola.calendula.domain.isDeclined import de.jeanlucmakiola.calendula.widget.glanceDeclinedDecoration import android.content.Context @@ -95,6 +98,11 @@ class AgendaWidget : GlanceAppWidget() { // the RemoteViews past the binder transaction limit. override val sizeMode = SizeMode.Single + override val previewSizeMode = previewSizeOf(WidgetKind.AGENDA) + + override suspend fun providePreview(context: Context, widgetCategory: Int) = + provideSamplePreview(context, WidgetKind.AGENDA) + override suspend fun provideGlance(context: Context, id: GlanceId) { val stamp = refreshStamp(context, id) val widgetData = WidgetData(context.loadAgendaWidget(id), stamp) diff --git a/app/src/main/java/de/jeanlucmakiola/calendula/widget/countdown/CountdownWidget.kt b/app/src/main/java/de/jeanlucmakiola/calendula/widget/countdown/CountdownWidget.kt index 26a63a9..7bca448 100644 --- a/app/src/main/java/de/jeanlucmakiola/calendula/widget/countdown/CountdownWidget.kt +++ b/app/src/main/java/de/jeanlucmakiola/calendula/widget/countdown/CountdownWidget.kt @@ -1,5 +1,8 @@ package de.jeanlucmakiola.calendula.widget.countdown +import de.jeanlucmakiola.calendula.widget.WidgetKind +import de.jeanlucmakiola.calendula.widget.previewSizeOf +import de.jeanlucmakiola.calendula.widget.provideSamplePreview import android.appwidget.AppWidgetManager import android.content.Context import android.content.res.Resources @@ -70,6 +73,11 @@ class CountdownWidget : GlanceAppWidget() { override val sizeMode = SizeMode.Single + override val previewSizeMode = previewSizeOf(WidgetKind.COUNTDOWN) + + override suspend fun providePreview(context: Context, widgetCategory: Int) = + provideSamplePreview(context, WidgetKind.COUNTDOWN) + override suspend fun provideGlance(context: Context, id: GlanceId) { val widgetData = WidgetData(context.loadPlacedCountdown(id), refreshStamp(context, id)) provideContent { diff --git a/app/src/main/java/de/jeanlucmakiola/calendula/widget/date/DateWidget.kt b/app/src/main/java/de/jeanlucmakiola/calendula/widget/date/DateWidget.kt index 70eef2e..e75120f 100644 --- a/app/src/main/java/de/jeanlucmakiola/calendula/widget/date/DateWidget.kt +++ b/app/src/main/java/de/jeanlucmakiola/calendula/widget/date/DateWidget.kt @@ -1,5 +1,8 @@ package de.jeanlucmakiola.calendula.widget.date +import de.jeanlucmakiola.calendula.widget.WidgetKind +import de.jeanlucmakiola.calendula.widget.previewSizeOf +import de.jeanlucmakiola.calendula.widget.provideSamplePreview import android.content.Context import androidx.compose.runtime.Composable import androidx.compose.ui.unit.dp @@ -59,6 +62,11 @@ class DateWidget : GlanceAppWidget() { override val sizeMode = SizeMode.Exact + override val previewSizeMode = previewSizeOf(WidgetKind.DATE) + + override suspend fun providePreview(context: Context, widgetCategory: Int) = + provideSamplePreview(context, WidgetKind.DATE) + override suspend fun provideGlance(context: Context, id: GlanceId) { val widgetData = WidgetData(context.loadToday(), refreshStamp(context, id)) provideContent { diff --git a/app/src/main/java/de/jeanlucmakiola/calendula/widget/month/MonthWidget.kt b/app/src/main/java/de/jeanlucmakiola/calendula/widget/month/MonthWidget.kt index 2b49d70..f45af96 100644 --- a/app/src/main/java/de/jeanlucmakiola/calendula/widget/month/MonthWidget.kt +++ b/app/src/main/java/de/jeanlucmakiola/calendula/widget/month/MonthWidget.kt @@ -1,5 +1,8 @@ package de.jeanlucmakiola.calendula.widget.month +import de.jeanlucmakiola.calendula.widget.WidgetKind +import de.jeanlucmakiola.calendula.widget.previewSizeOf +import de.jeanlucmakiola.calendula.widget.provideSamplePreview import android.content.Context import androidx.compose.runtime.Composable import androidx.compose.runtime.SideEffect @@ -43,7 +46,6 @@ import androidx.glance.layout.fillMaxWidth import androidx.glance.layout.height import androidx.glance.layout.padding import androidx.glance.layout.size -import androidx.glance.layout.width import androidx.glance.state.PreferencesGlanceStateDefinition import de.jeanlucmakiola.floret.glance.LocalWidgetThemeMode import de.jeanlucmakiola.floret.glance.WidgetColorRole @@ -136,6 +138,11 @@ class MonthWidget : GlanceAppWidget() { */ override val sizeMode = SizeMode.Exact + override val previewSizeMode = previewSizeOf(WidgetKind.MONTH) + + override suspend fun providePreview(context: Context, widgetCategory: Int) = + provideSamplePreview(context, WidgetKind.MONTH) + override suspend fun provideGlance(context: Context, id: GlanceId) { val shown = getAppWidgetState(context, id)[MONTH_INDEX_KEY] ?: currentMonthIndex(systemZone()) @@ -239,8 +246,6 @@ private fun MonthWidgetBody(source: MonthWidgetSource, index: Int) { is MonthWidgetSource.Ready -> { val zone = systemZone() val ym = yearMonthOf(index) - // Column width from the live widget size, minus our H padding. - val colW = (LocalSize.current.width - GRID_HPADDING * 2) / 7 val weeks = layoutMonthWeeks(ym, source.weekStart, source.instances, zone) val context = LocalContext.current val fontScale = context.resources.configuration.fontScale @@ -261,7 +266,7 @@ private fun MonthWidgetBody(source: MonthWidgetSource, index: Int) { // each carry their own fill, so a transparent widget stays // readable without one blob over the wallpaper (#292). Column(modifier = GlanceModifier.fillMaxWidth().defaultWeight()) { - WeekdayHeader(weekStart = source.weekStart, colW = colW) + WeekdayHeader(weekStart = source.weekStart) AndroidRemoteViews( remoteViews = monthGridViews(context, layouts, ym.month, source.today, source.soften, colors, mode), modifier = GlanceModifier.fillMaxWidth().defaultWeight(), @@ -315,10 +320,10 @@ private fun MonthHeader(label: String, today: LocalDate) { } @Composable -private fun WeekdayHeader(weekStart: DayOfWeek, colW: Dp) { +private fun WeekdayHeader(weekStart: DayOfWeek) { Row(modifier = GlanceModifier.fillMaxWidth().padding(bottom = 1.dp)) { weekdayNarrowNames(weekStart).forEach { name -> - Box(modifier = GlanceModifier.width(colW).padding(horizontal = 1.dp)) { + Box(modifier = GlanceModifier.defaultWeight().padding(horizontal = 1.dp)) { Text( text = name, style = TextStyle( diff --git a/app/src/main/java/de/jeanlucmakiola/calendula/widget/quickadd/QuickAddWidget.kt b/app/src/main/java/de/jeanlucmakiola/calendula/widget/quickadd/QuickAddWidget.kt index 83535de..97a4642 100644 --- a/app/src/main/java/de/jeanlucmakiola/calendula/widget/quickadd/QuickAddWidget.kt +++ b/app/src/main/java/de/jeanlucmakiola/calendula/widget/quickadd/QuickAddWidget.kt @@ -1,5 +1,8 @@ package de.jeanlucmakiola.calendula.widget.quickadd +import de.jeanlucmakiola.calendula.widget.WidgetKind +import de.jeanlucmakiola.calendula.widget.previewSizeOf +import de.jeanlucmakiola.calendula.widget.provideSamplePreview import android.content.Context import androidx.compose.runtime.Composable import androidx.compose.ui.unit.dp @@ -46,6 +49,11 @@ class QuickAddWidget : GlanceAppWidget() { // Exact: the shape fills whatever square the launcher gives it. override val sizeMode = SizeMode.Exact + override val previewSizeMode = previewSizeOf(WidgetKind.QUICK_ADD) + + override suspend fun providePreview(context: Context, widgetCategory: Int) = + provideSamplePreview(context, WidgetKind.QUICK_ADD) + override suspend fun provideGlance(context: Context, id: GlanceId) { val widgetData = WidgetData(context.loadToday(), refreshStamp(context, id)) provideContent { diff --git a/app/src/main/java/de/jeanlucmakiola/calendula/widget/split/MonthDayWidget.kt b/app/src/main/java/de/jeanlucmakiola/calendula/widget/split/MonthDayWidget.kt index 9f12100..b6ef071 100644 --- a/app/src/main/java/de/jeanlucmakiola/calendula/widget/split/MonthDayWidget.kt +++ b/app/src/main/java/de/jeanlucmakiola/calendula/widget/split/MonthDayWidget.kt @@ -1,5 +1,8 @@ package de.jeanlucmakiola.calendula.widget.split +import de.jeanlucmakiola.calendula.widget.WidgetKind +import de.jeanlucmakiola.calendula.widget.previewSizeOf +import de.jeanlucmakiola.calendula.widget.provideSamplePreview import android.content.Context import androidx.compose.runtime.Composable import androidx.compose.ui.graphics.Color @@ -146,6 +149,11 @@ class MonthDayWidget : GlanceAppWidget() { override val sizeMode = SizeMode.Exact + override val previewSizeMode = previewSizeOf(WidgetKind.MONTH_DAY) + + override suspend fun providePreview(context: Context, widgetCategory: Int) = + provideSamplePreview(context, WidgetKind.MONTH_DAY) + override suspend fun provideGlance(context: Context, id: GlanceId) { val stamp = refreshStamp(context, id) val shown = context.widgetState(id)[MONTH_INDEX_KEY] ?: monthIndexOf(today(systemZone())) diff --git a/app/src/main/java/de/jeanlucmakiola/calendula/widget/today/TodayWidget.kt b/app/src/main/java/de/jeanlucmakiola/calendula/widget/today/TodayWidget.kt index db9ec26..bd6e757 100644 --- a/app/src/main/java/de/jeanlucmakiola/calendula/widget/today/TodayWidget.kt +++ b/app/src/main/java/de/jeanlucmakiola/calendula/widget/today/TodayWidget.kt @@ -1,5 +1,8 @@ package de.jeanlucmakiola.calendula.widget.today +import de.jeanlucmakiola.calendula.widget.WidgetKind +import de.jeanlucmakiola.calendula.widget.previewSizeOf +import de.jeanlucmakiola.calendula.widget.provideSamplePreview import android.content.Context import androidx.compose.runtime.Composable import androidx.compose.ui.unit.dp @@ -87,6 +90,11 @@ class TodayWidget : GlanceAppWidget() { // Exact: whether one or two of tomorrow's events fit follows the height. override val sizeMode = SizeMode.Exact + override val previewSizeMode = previewSizeOf(WidgetKind.TODAY) + + override suspend fun providePreview(context: Context, widgetCategory: Int) = + provideSamplePreview(context, WidgetKind.TODAY) + override suspend fun provideGlance(context: Context, id: GlanceId) { val widgetData = WidgetData(context.loadTodayList(context.widgetLoadContext(id)), refreshStamp(context, id)) provideContent { @@ -165,7 +173,8 @@ private fun TodayList(events: List, data: DaysWidgetData.Ready, m ) } if (preview) { - Column(modifier = GlanceModifier.fillMaxSize()) { repeat(minOf(events.size, 10)) { row(it) } } + val fits = ((LocalSize.current.height - 12.dp + RowGap) / (metrics.rowHeight + RowGap)).toInt().coerceAtLeast(1) + Column(modifier = GlanceModifier.fillMaxSize()) { repeat(minOf(events.size, fits)) { row(it) } } } else { LazyColumn(modifier = GlanceModifier.fillMaxSize()) { items(events.size) { row(it) } } } diff --git a/app/src/main/java/de/jeanlucmakiola/calendula/widget/upcoming/UpcomingWidget.kt b/app/src/main/java/de/jeanlucmakiola/calendula/widget/upcoming/UpcomingWidget.kt index cc04d2c..1f4ddd7 100644 --- a/app/src/main/java/de/jeanlucmakiola/calendula/widget/upcoming/UpcomingWidget.kt +++ b/app/src/main/java/de/jeanlucmakiola/calendula/widget/upcoming/UpcomingWidget.kt @@ -1,5 +1,8 @@ package de.jeanlucmakiola.calendula.widget.upcoming +import de.jeanlucmakiola.calendula.widget.WidgetKind +import de.jeanlucmakiola.calendula.widget.previewSizeOf +import de.jeanlucmakiola.calendula.widget.provideSamplePreview import android.content.Context import androidx.compose.runtime.Composable import androidx.compose.ui.unit.Dp @@ -77,6 +80,11 @@ class UpcomingWidget : GlanceAppWidget() { // Exact: how many rows show follows the height. override val sizeMode = SizeMode.Exact + override val previewSizeMode = previewSizeOf(WidgetKind.UPCOMING) + + override suspend fun providePreview(context: Context, widgetCategory: Int) = + provideSamplePreview(context, WidgetKind.UPCOMING) + override suspend fun provideGlance(context: Context, id: GlanceId) { val widgetData = WidgetData(context.loadUpcoming(context.widgetLoadContext(id)), refreshStamp(context, id)) provideContent { diff --git a/app/src/main/java/de/jeanlucmakiola/calendula/widget/week/WeekStripWidget.kt b/app/src/main/java/de/jeanlucmakiola/calendula/widget/week/WeekStripWidget.kt index 1b48e28..eac2150 100644 --- a/app/src/main/java/de/jeanlucmakiola/calendula/widget/week/WeekStripWidget.kt +++ b/app/src/main/java/de/jeanlucmakiola/calendula/widget/week/WeekStripWidget.kt @@ -1,5 +1,8 @@ package de.jeanlucmakiola.calendula.widget.week +import de.jeanlucmakiola.calendula.widget.WidgetKind +import de.jeanlucmakiola.calendula.widget.previewSizeOf +import de.jeanlucmakiola.calendula.widget.provideSamplePreview import android.content.Context import androidx.compose.runtime.Composable import androidx.compose.ui.unit.dp @@ -79,6 +82,11 @@ class WeekStripWidget : GlanceAppWidget() { override val sizeMode = SizeMode.Single + override val previewSizeMode = previewSizeOf(WidgetKind.WEEK_STRIP) + + override suspend fun providePreview(context: Context, widgetCategory: Int) = + provideSamplePreview(context, WidgetKind.WEEK_STRIP) + override suspend fun provideGlance(context: Context, id: GlanceId) { val widgetData = WidgetData(context.loadWeek(id), refreshStamp(context, id)) provideContent { diff --git a/app/src/main/res/drawable/preview_stripe.xml b/app/src/main/res/drawable/preview_stripe.xml index 9351a43..f1be56b 100644 --- a/app/src/main/res/drawable/preview_stripe.xml +++ b/app/src/main/res/drawable/preview_stripe.xml @@ -1,6 +1,6 @@ - + diff --git a/app/src/main/res/drawable/preview_widget_cell_bg.xml b/app/src/main/res/drawable/preview_widget_cell_bg.xml new file mode 100644 index 0000000..2d8aea5 --- /dev/null +++ b/app/src/main/res/drawable/preview_widget_cell_bg.xml @@ -0,0 +1,6 @@ + + + + + diff --git a/app/src/main/res/drawable/preview_widget_row_bg.xml b/app/src/main/res/drawable/preview_widget_event_bg.xml similarity index 60% rename from app/src/main/res/drawable/preview_widget_row_bg.xml rename to app/src/main/res/drawable/preview_widget_event_bg.xml index 79ed94e..0660e55 100644 --- a/app/src/main/res/drawable/preview_widget_row_bg.xml +++ b/app/src/main/res/drawable/preview_widget_event_bg.xml @@ -1,6 +1,6 @@ - - + + diff --git a/app/src/main/res/drawable/preview_widget_outside_bg.xml b/app/src/main/res/drawable/preview_widget_outside_bg.xml new file mode 100644 index 0000000..8882d92 --- /dev/null +++ b/app/src/main/res/drawable/preview_widget_outside_bg.xml @@ -0,0 +1,6 @@ + + + + + diff --git a/app/src/main/res/layout/widget_preview_agenda.xml b/app/src/main/res/layout/widget_preview_agenda.xml index 75721fe..aceb98d 100644 --- a/app/src/main/res/layout/widget_preview_agenda.xml +++ b/app/src/main/res/layout/widget_preview_agenda.xml @@ -1,124 +1,186 @@ - + - - - - + android:orientation="vertical" + android:padding="8dp" + tools:ignore="HardcodedText,NestedWeights,DisableBaselineAlignment,SmallSp,TooManyViews,UselessParent"> - + android:gravity="center_vertical" + android:orientation="horizontal"> + + + + + + + + android:layout_marginTop="0dp" + android:background="@drawable/preview_widget_event_bg" + android:backgroundTint="@color/widget_preview_event_blue" + android:orientation="vertical" + android:paddingHorizontal="10dp" + android:paddingVertical="3dp"> + android:ellipsize="end" + android:maxLines="1" + android:text="@string/settings_past_events_sample_morning" + android:textColor="@color/widget_preview_on_event_light" + android:textSize="12sp" + android:textStyle="bold" /> + android:ellipsize="end" + android:maxLines="1" + android:text="9:30 – 10:00" + android:textColor="@color/widget_preview_on_event_light" + android:textSize="11sp" /> + + + + + + + + + - + android:layout_marginTop="6dp" + android:orientation="horizontal"> + + + android:layout_marginTop="0dp" + android:background="@drawable/preview_widget_event_bg" + android:backgroundTint="@color/widget_preview_event_orange" + android:orientation="vertical" + android:paddingHorizontal="10dp" + android:paddingVertical="3dp"> + android:ellipsize="end" + android:maxLines="1" + android:text="@string/widget_preview_sample_trip" + android:textColor="@color/widget_preview_on_event_light" + android:textSize="12sp" + android:textStyle="bold" /> + android:ellipsize="end" + android:maxLines="1" + android:text="@string/event_detail_all_day" + android:textColor="@color/widget_preview_on_event_light" + android:textSize="11sp" /> - - - - - - - diff --git a/app/src/main/res/layout/widget_preview_countdown.xml b/app/src/main/res/layout/widget_preview_countdown.xml index f6d65ef..966dbc3 100644 --- a/app/src/main/res/layout/widget_preview_countdown.xml +++ b/app/src/main/res/layout/widget_preview_countdown.xml @@ -1,27 +1,49 @@ - + + android:orientation="horizontal" + android:padding="12dp" + tools:ignore="HardcodedText,NestedWeights,DisableBaselineAlignment,SmallSp,TooManyViews,UselessParent"> - + - + android:layout_marginStart="10dp" + android:layout_weight="1" + android:orientation="vertical"> + + + + diff --git a/app/src/main/res/layout/widget_preview_date.xml b/app/src/main/res/layout/widget_preview_date.xml index ed0b51b..a5d8409 100644 --- a/app/src/main/res/layout/widget_preview_date.xml +++ b/app/src/main/res/layout/widget_preview_date.xml @@ -1,5 +1,6 @@ - + diff --git a/app/src/main/res/layout/widget_preview_month.xml b/app/src/main/res/layout/widget_preview_month.xml index 90c2d0c..910375a 100644 --- a/app/src/main/res/layout/widget_preview_month.xml +++ b/app/src/main/res/layout/widget_preview_month.xml @@ -1,114 +1,776 @@ - + + android:orientation="vertical" + android:padding="8dp" + tools:ignore="HardcodedText,NestedWeights,DisableBaselineAlignment,SmallSp,TooManyViews,UselessParent"> - - + android:gravity="center_vertical" + android:orientation="horizontal"> - + + + + - - - - - - - - + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/widget_preview_month_day.xml b/app/src/main/res/layout/widget_preview_month_day.xml index 51cc676..451a6cd 100644 --- a/app/src/main/res/layout/widget_preview_month_day.xml +++ b/app/src/main/res/layout/widget_preview_month_day.xml @@ -1,38 +1,422 @@ - + + android:padding="8dp" + tools:ignore="HardcodedText,NestedWeights,DisableBaselineAlignment,SmallSp,TooManyViews,UselessParent"> - + android:gravity="center_vertical" + android:orientation="horizontal"> + + + + + + - + android:layout_weight="1.1" + android:orientation="vertical"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/widget_preview_quick_add.xml b/app/src/main/res/layout/widget_preview_quick_add.xml index d2c8211..a896785 100644 --- a/app/src/main/res/layout/widget_preview_quick_add.xml +++ b/app/src/main/res/layout/widget_preview_quick_add.xml @@ -1,5 +1,6 @@ - + - + + tools:ignore="HardcodedText,NestedWeights,DisableBaselineAlignment,SmallSp,TooManyViews,UselessParent"> @@ -25,7 +26,59 @@ android:layout_weight="1" android:gravity="center_vertical" android:orientation="vertical"> - - + + + + + + + + diff --git a/app/src/main/res/layout/widget_preview_upcoming.xml b/app/src/main/res/layout/widget_preview_upcoming.xml index 5004888..b714508 100644 --- a/app/src/main/res/layout/widget_preview_upcoming.xml +++ b/app/src/main/res/layout/widget_preview_upcoming.xml @@ -1,23 +1,89 @@ - + + android:padding="8dp" + tools:ignore="HardcodedText,NestedWeights,DisableBaselineAlignment,SmallSp,TooManyViews,UselessParent"> - + android:gravity="center_vertical" + android:orientation="horizontal" + android:paddingHorizontal="4dp"> + + + - - - + + + + + + + + diff --git a/app/src/main/res/layout/widget_preview_week_strip.xml b/app/src/main/res/layout/widget_preview_week_strip.xml index f14b3cb..e2a0717 100644 --- a/app/src/main/res/layout/widget_preview_week_strip.xml +++ b/app/src/main/res/layout/widget_preview_week_strip.xml @@ -1,24 +1,202 @@ - + + tools:ignore="HardcodedText,NestedWeights,DisableBaselineAlignment,SmallSp,TooManyViews,UselessParent"> - - - - - - - + android:orientation="vertical"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/values-night-v31/widget_preview_colors.xml b/app/src/main/res/values-night-v31/widget_preview_colors.xml index 323de5f..f987341 100644 --- a/app/src/main/res/values-night-v31/widget_preview_colors.xml +++ b/app/src/main/res/values-night-v31/widget_preview_colors.xml @@ -1,9 +1,10 @@ - @android:color/system_neutral1_900 + @android:color/system_accent2_700 + @android:color/system_accent2_900 + @android:color/system_neutral2_600 @android:color/system_neutral1_50 @android:color/system_neutral2_200 @android:color/system_accent1_200 @android:color/system_accent1_800 - @android:color/system_accent2_800 diff --git a/app/src/main/res/values-night/widget_preview_colors.xml b/app/src/main/res/values-night/widget_preview_colors.xml index 233d427..7df62c3 100644 --- a/app/src/main/res/values-night/widget_preview_colors.xml +++ b/app/src/main/res/values-night/widget_preview_colors.xml @@ -1,9 +1,10 @@ - #101316 + #2B3742 + #1C252D + #3A4651 #E1E3E6 #A8ADB2 #A3CBE2 #003348 - #2D3236 diff --git a/app/src/main/res/values-v31/widget_preview_colors.xml b/app/src/main/res/values-v31/widget_preview_colors.xml index 902c1e0..f706cb2 100644 --- a/app/src/main/res/values-v31/widget_preview_colors.xml +++ b/app/src/main/res/values-v31/widget_preview_colors.xml @@ -1,9 +1,10 @@ - @android:color/system_neutral1_50 + @android:color/system_accent2_100 + @android:color/system_accent2_10 + @android:color/system_neutral2_300 @android:color/system_neutral1_900 @android:color/system_neutral2_700 @android:color/system_accent1_600 @android:color/system_accent1_0 - @android:color/system_accent2_100 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a9f1e0e..1e940ae 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -621,6 +621,9 @@ Team review Lunch with Robin Choir practice + + Weekend trip + Dentist Agenda Agenda range How far ahead the Agenda screen lists events. diff --git a/app/src/main/res/values/widget_preview_colors.xml b/app/src/main/res/values/widget_preview_colors.xml index 69ddbb1..b584476 100644 --- a/app/src/main/res/values/widget_preview_colors.xml +++ b/app/src/main/res/values/widget_preview_colors.xml @@ -1,12 +1,21 @@ - + - #FBFCFE + #D6E4F0 + #EEF3F8 + #C2CCD6 #191C1F #6E7479 #3B5364 #FFFFFF - #E1E6EB + #039BE5 + #33B679 + #8E24AA + #F4511E + #D50000 + #000000 + #FFFFFF diff --git a/app/src/main/res/values/widget_preview_styles.xml b/app/src/main/res/values/widget_preview_styles.xml index d4d081b..0362d0f 100644 --- a/app/src/main/res/values/widget_preview_styles.xml +++ b/app/src/main/res/values/widget_preview_styles.xml @@ -1,23 +1,29 @@ + + - diff --git a/floret-kit b/floret-kit index f2ffaed..6b5ee40 160000 --- a/floret-kit +++ b/floret-kit @@ -1 +1 @@ -Subproject commit f2ffaed9259180051a5564e7ad10589139cce03a +Subproject commit 6b5ee409b3068f2e7a78be7dea80104fed502cd8