Compare commits

...
Author SHA1 Message Date
makiolaj 14c10c6d0b 2.24.0-beta.2
Beta — Codeberg pre-release / detect (push) Successful in 5s
Beta — Codeberg pre-release / beta (push) Successful in 10m9s
2026-10-28 15:35:38 +01:00
Jean-Luc Makiolaandmakiolaj f5e28eae8d Widget fixes and tweaks from the 2.24 beta feedback (#449)
Beta — Codeberg pre-release / detect (push) Successful in 5s
Beta — Codeberg pre-release / beta (push) Skipped
Feedback on 2.24.0-beta.1 from #90, #376 and #71. floret-kit is pinned to its main after floret-kit#24, which carries the shared parts.

- Transparent background: switching to or from it now shows right away on Date, Countdown, Week strip, Quick add, Upcoming and Today (and the Month and day grid). Their backings always set a fill, so a reapplied rendering can't keep the old one.
- App language: changing it in Settings redraws every widget, which now render in the app language. Below Android 13 the choice is mirrored for the widget process, since AppCompat only applies it inside an activity.
- Picker previews: Countdown and Week strip previews inflate on Android 14 and below (they used View/Space, which RemoteViews refuses).
- Month widget: days outside the month take a 40% lighter shade, in the widget and its XML preview.
- Add button: the filled header action is 44dp instead of 36dp wide (floret-kit).
- Tinted background: the tonal header and weekday pills had the same tone as the fill and disappeared; they now use inversePrimary there (floret-kit). Not in the issues, found while testing.
- Dimmed past events (Month and day, Today, Agenda): the fill fades to 30% and the text to 65% instead of both to 40%, so the title stays readable.
- Widget picker: names drop the Calendula prefix, and each widget gets its own description instead of repeating its name (#71).

The config screen stalling until touched (#90, item 2) is not addressed: it didn't reproduce on an Android 12 emulator.

Closes #376
Closes #71

Co-authored-by: Jean-Luc Makiola <business@jeanlucmakiola.de>
Reviewed-on: https://codeberg.org/jlmakiola/calendula/pulls/449
2026-10-28 15:34:16 +01:00
37 changed files with 179 additions and 118 deletions
+3
View File
@@ -16,9 +16,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- **The agenda and month widgets have a new look.** The agenda puts each day in its own card, with a round date pill beside the first event and today's pill filled. The month widget has rounded day cells, weekday pills, shaded days outside the month and a + for a new event. The agenda's refresh button is gone, since widgets now update on their own ([#71], [#90]).
- **Tapping a day in the month widget opens that day,** wherever in its cell you tap ([#386]).
- **The widget picker shows what each widget looks like.** On Android 15 and later the previews are the real widgets with sample events, in your language and colours; on older versions they are redrawn to match.
- **Widgets are listed by their name alone,** without "Calendula" in front, and each has a short description in the picker ([#71]).
- **Past events in widgets stay readable when dimmed.** Their background fades further and their text less ([#90]).
### Fixed
- **Widgets show your changes straight away.** After you added, edited or deleted an event, a widget could keep showing the old events until something else redrew it ([#420], [#229]).
- **Widgets follow the app's language.** After you picked a language in Settings, widgets kept the old one until something redrew them ([#90]).
- **The month widget handles busy months.** It could show "Can't show content", or only one or two events per day. Each day now shows as many events as its height fits, and the rest fold into +N ([#372]).
## [2.23.0] — 2026-10-21
+2 -2
View File
@@ -32,8 +32,8 @@ android {
// (up to 2.22.3: 2.22.3 -> 22203; from 2.22.4: 2.23.0-beta.1 -> 2230001,
// 2.22.4 -> 2220499), and CI fails if the committed one doesn't match.
// See docs/RELEASING.md.
versionCode = 2240001
versionName = "2.24.0-beta.1"
versionCode = 2240002
versionName = "2.24.0-beta.2"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
@@ -15,6 +15,7 @@ import de.jeanlucmakiola.calendula.widget.syncWidgetRollover
import de.jeanlucmakiola.calendula.widget.widgetEntryPoint
import de.jeanlucmakiola.floret.crash.CrashConfig
import de.jeanlucmakiola.floret.crash.CrashReporter
import de.jeanlucmakiola.floret.locale.AppLanguage
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
@@ -45,6 +46,9 @@ class CalendulaApp : Application() {
chooseIssueUrl = getString(R.string.report_issue_choose_url),
),
)
// Below API 33 only AppCompat activities get the app language; a process
// woken for a widget formats in it too (#90).
AppLanguage.applyDefault(this)
// Before anything that writes, so the widgets see every change (#420).
widgetEntryPoint().widgetRefreshCoordinator().start()
reconcileAutoBackup()
@@ -30,6 +30,7 @@ import androidx.lifecycle.lifecycleScope
import dagger.hilt.android.AndroidEntryPoint
import de.jeanlucmakiola.calendula.data.calendar.CalendarRepository
import de.jeanlucmakiola.calendula.data.prefs.SettingsPrefs
import de.jeanlucmakiola.floret.locale.AppLanguage
import de.jeanlucmakiola.floret.prefs.ThemeMode
import de.jeanlucmakiola.floret.prefs.isDark
import de.jeanlucmakiola.floret.prefs.isPureBlack
@@ -278,6 +279,7 @@ class MainActivity : AppCompatActivity() {
super.onResume()
// Reaching a running UI means startup succeeded; reset the loop trail.
CrashReporter.markHealthy(this)
AppLanguage.syncMirror(this)
}
override fun onSaveInstanceState(outState: Bundle) {
@@ -63,6 +63,7 @@ import de.jeanlucmakiola.floret.components.GroupedListInset
import de.jeanlucmakiola.floret.components.GroupedRow
import de.jeanlucmakiola.floret.components.GroupedSectionHeader
import de.jeanlucmakiola.floret.components.LanguagePickerRow
import de.jeanlucmakiola.calendula.widget.requestWidgetRedraw
import de.jeanlucmakiola.floret.components.Position
import de.jeanlucmakiola.floret.crash.CrashReportDialog
import de.jeanlucmakiola.floret.crash.CrashReporter
@@ -258,15 +259,16 @@ private fun SettingsHub(
leading = { CategoryIcon(Icons.Default.Widgets, ChipAccent.Tertiary) },
onClick = { onOpenSection(SettingsSection.Widgets) },
)
val context = LocalContext.current
LanguagePickerRow(
position = Position.Middle,
title = stringResource(R.string.settings_language),
autoLabel = stringResource(R.string.settings_language_auto),
localesConfig = R.xml.locales_config,
predictiveBack = true,
onChange = { requestWidgetRedraw(context) },
// Invite contributions right where users pick their language.
header = {
val context = LocalContext.current
val translateUrl = stringResource(R.string.about_translate_url)
GroupedRow(
title = stringResource(R.string.settings_translate),
@@ -23,7 +23,6 @@ import de.jeanlucmakiola.calendula.R
import de.jeanlucmakiola.calendula.domain.EventInstance
import de.jeanlucmakiola.calendula.domain.isDeclined
import de.jeanlucmakiola.calendula.ui.common.CalendarView
import de.jeanlucmakiola.calendula.ui.common.EventDimAlpha
import de.jeanlucmakiola.calendula.ui.common.SECONDARY_INK_ALPHA
import de.jeanlucmakiola.calendula.ui.common.eventFill
import de.jeanlucmakiola.calendula.ui.common.eventInk
@@ -32,6 +31,16 @@ import de.jeanlucmakiola.floret.components.Position
import de.jeanlucmakiola.floret.glance.WidgetGroupedRow
import de.jeanlucmakiola.floret.glance.themedColor
/** A past event's fill when dimmed: fainter than the app's whole-row dim, so its text can stay readable (#90). */
internal const val WidgetDimFillAlpha = 0.3f
/** A dimmed event's ink, held above its fill. */
private const val WidgetDimInkAlpha = 0.65f
/** Ink for an event on [fill], picked against the opaque tone and faded when the fill is dimmed. */
internal fun widgetEventInk(fill: Color, alpha: Float): Color =
eventInk(fill.copy(alpha = 1f), if (fill.alpha < 1f) alpha * WidgetDimInkAlpha else alpha)
/** Text and row sizes of a compact event list, from a widget's [WidgetSize]. */
internal data class EventRowMetrics(val title: TextUnit, val time: TextUnit, val rowHeight: Dp, val message: TextUnit)
@@ -63,11 +72,9 @@ internal fun WidgetEventRow(
quiet: Boolean = false,
) {
val context = LocalContext.current
fun tone(dark: Boolean): Color = eventFill(event.color, dark, soften).let { if (dimmed) it.copy(alpha = EventDimAlpha) else it }
fun tone(dark: Boolean): Color = eventFill(event.color, dark, soften).let { if (dimmed) it.copy(alpha = WidgetDimFillAlpha) else it }
val day = tone(dark = false)
val night = tone(dark = true)
// Ink is picked against the opaque fill, then carries the dim along with it.
fun ink(fill: Color, alpha: Float) = eventInk(fill.copy(alpha = 1f), alpha * fill.alpha)
val card = widgetCardColor()
WidgetGroupedRow(position = position, color = if (quiet) card else themedColor(day = day, night = night)) {
Column(
@@ -91,12 +98,12 @@ internal fun WidgetEventRow(
val titleInk = if (quiet) {
GlanceTheme.colors.onSurface
} else {
themedColor(day = ink(day, 1f), night = ink(night, 1f))
themedColor(day = widgetEventInk(day, 1f), night = widgetEventInk(night, 1f))
}
val timeInk = if (quiet) {
GlanceTheme.colors.onSurfaceVariant
} else {
themedColor(day = ink(day, SECONDARY_INK_ALPHA), night = ink(night, SECONDARY_INK_ALPHA))
themedColor(day = widgetEventInk(day, SECONDARY_INK_ALPHA), night = widgetEventInk(night, SECONDARY_INK_ALPHA))
}
Text(
text = event.title.ifBlank { context.getString(R.string.event_untitled) },
@@ -34,6 +34,17 @@ internal val WIDGET_RECEIVER_CLASSES: List<Class<*>> = WidgetKind.entries.map {
/** The app's own midnight wake-up (#228). */
internal const val ACTION_WIDGET_ROLLOVER = "de.jeanlucmakiola.calendula.widget.ROLLOVER"
/** Redraws every widget, for app changes they draw with that no other signal reaches, like its language (#90). */
internal const val ACTION_WIDGET_REDRAW = "de.jeanlucmakiola.calendula.widget.REDRAW"
/**
* Asks [WidgetUpdateReceiver] to redraw every widget. A broadcast rather than a
* coroutine, so it outlives the activity a language change recreates.
*/
fun requestWidgetRedraw(context: Context) {
context.sendBroadcast(Intent(context, WidgetUpdateReceiver::class.java).setAction(ACTION_WIDGET_REDRAW))
}
/** Fixed: there is only ever one rollover alarm, and re-arming must replace it. */
private const val ROLLOVER_REQUEST_CODE = 0x0DA1
@@ -1,12 +1,17 @@
package de.jeanlucmakiola.calendula.widget
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.remember
import androidx.compose.runtime.staticCompositionLocalOf
import androidx.glance.LocalContext
import androidx.glance.text.TextDecoration
import de.jeanlucmakiola.calendula.ui.theme.CalendulaDarkFallback
import de.jeanlucmakiola.calendula.ui.theme.CalendulaLightFallback
import de.jeanlucmakiola.floret.glance.FloretGlanceTheme
import de.jeanlucmakiola.floret.glance.WidgetSurface
import de.jeanlucmakiola.floret.glance.WidgetThemeMode
import de.jeanlucmakiola.floret.locale.AppLanguage
import de.jeanlucmakiola.floret.prefs.ThemeMode
/**
@@ -39,3 +44,20 @@ internal fun CalendulaWidgetRoot(appearance: WidgetAppearance, appTheme: ThemeMo
*/
fun glanceDeclinedDecoration(isDeclined: Boolean): TextDecoration =
if (isDeclined) TextDecoration.LineThrough else TextDecoration.None
/** Static, so a language change recomposes the whole widget rather than letting unchanged parts skip. */
private val LocalWidgetLanguage = staticCompositionLocalOf<String?> { null }
/**
* Draws [content] in the app's own language (#90): [LocalContext] and the
* default locale follow it, which below API 33 nothing else does outside an
* AppCompat activity. Read on every recomposition, so a redraw picks up a change.
*/
@Composable
internal fun InAppLanguage(content: @Composable () -> Unit) {
val base = LocalContext.current
val tag = AppLanguage.currentTag(base)
AppLanguage.applyDefault(base, tag)
val localized = remember(base, tag) { AppLanguage.localized(base, tag) }
CompositionLocalProvider(LocalContext provides localized, LocalWidgetLanguage provides tag, content = content)
}
@@ -4,10 +4,11 @@ import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import de.jeanlucmakiola.floret.glance.FloretWidgetUpdateReceiver
import de.jeanlucmakiola.floret.locale.AppLanguage
import kotlin.time.Duration.Companion.minutes
/**
* Redraws both widgets when their data, day or language goes stale, and keeps
* Redraws the widgets when their data, day or language goes stale, and keeps
* the midnight rollover armed. `PROVIDER_CHANGED` covers changes made outside
* the app; the app's own writes go through [WidgetRefreshCoordinator]. A data
* change also re-arms, at most every 15 minutes: the inexact alarm can be
@@ -18,12 +19,15 @@ import kotlin.time.Duration.Companion.minutes
class WidgetUpdateReceiver : FloretWidgetUpdateReceiver() {
override val rolloverAction = ACTION_WIDGET_ROLLOVER
override val widgetReceiverClasses = WIDGET_RECEIVER_CLASSES
override val extraHandledActions = setOf(Intent.ACTION_PROVIDER_CHANGED)
override val extraHandledActions = setOf(Intent.ACTION_PROVIDER_CHANGED, ACTION_WIDGET_REDRAW)
override val extraRearmInterval = 15.minutes
override fun rolloverPendingIntent(context: Context): PendingIntent = widgetRolloverPendingIntent(context)
override fun skipRedraw(action: String) = action == Intent.ACTION_PROVIDER_CHANGED && isProviderEcho()
override suspend fun redraw(context: Context) = redrawWidgets(context)
override suspend fun redraw(context: Context) {
AppLanguage.applyDefault(context)
redrawWidgets(context)
}
}
@@ -1,5 +1,6 @@
package de.jeanlucmakiola.calendula.widget.agenda
import de.jeanlucmakiola.calendula.widget.InAppLanguage
import de.jeanlucmakiola.calendula.widget.WidgetKind
import de.jeanlucmakiola.calendula.widget.previewSizeOf
import de.jeanlucmakiola.calendula.widget.provideSamplePreview
@@ -50,11 +51,11 @@ import de.jeanlucmakiola.calendula.domain.EventInstance
import de.jeanlucmakiola.calendula.domain.hasEnded
import de.jeanlucmakiola.calendula.ui.agenda.anchorTodayIfMissing
import de.jeanlucmakiola.calendula.ui.common.CalendarView
import de.jeanlucmakiola.calendula.ui.common.EventDimAlpha
import de.jeanlucmakiola.calendula.ui.common.formatCalendarTitle
import de.jeanlucmakiola.floret.locale.localizedDateFormatter
import de.jeanlucmakiola.calendula.ui.common.eventFill
import de.jeanlucmakiola.calendula.ui.common.eventInk
import de.jeanlucmakiola.calendula.widget.WidgetDimFillAlpha
import de.jeanlucmakiola.calendula.widget.widgetEventInk
import de.jeanlucmakiola.calendula.ui.common.SECONDARY_INK_ALPHA
import de.jeanlucmakiola.calendula.widget.AgendaWidgetData
import de.jeanlucmakiola.calendula.widget.WidgetSize
@@ -110,11 +111,13 @@ class AgendaWidget : GlanceAppWidget() {
// A running session never re-runs this preamble, so every redraw
// moves the stamp and the data reloads here instead (#420).
val loaded = widgetData.current(currentRefreshStamp()) { context.loadAgendaWidget(id) }
AgendaWidgetContent(
data = loaded.content,
appearance = currentState<Preferences>().widgetAppearance,
appTheme = loaded.appTheme,
)
InAppLanguage {
AgendaWidgetContent(
data = loaded.content,
appearance = currentState<Preferences>().widgetAppearance,
appTheme = loaded.appTheme,
)
}
}
}
}
@@ -392,7 +395,7 @@ private data class EventFill(val day: Color, val night: Color)
private fun AgendaRow.Event.fill(data: AgendaWidgetData.Ready): EventFill {
val dimmed = data.pastDisplay == PastEventDisplay.DIM && event.hasEnded(data.now)
fun tone(dark: Boolean) = eventFill(event.color, dark, data.soften).let {
if (dimmed) it.copy(alpha = EventDimAlpha) else it
if (dimmed) it.copy(alpha = WidgetDimFillAlpha) else it
}
return EventFill(day = tone(dark = false), night = tone(dark = true))
}
@@ -407,12 +410,10 @@ private fun EventRow(
) {
val context = androidx.glance.LocalContext.current
val title = event.title.ifBlank { context.getString(R.string.event_untitled) }
// Ink is picked against the opaque fill, then carries the dim along with it.
fun ink(fill: Color, alpha: Float) = eventInk(fill.copy(alpha = 1f), alpha * fill.alpha)
val titleInk = themedColor(day = ink(fill.day, 1f), night = ink(fill.night, 1f))
val titleInk = themedColor(day = widgetEventInk(fill.day, 1f), night = widgetEventInk(fill.night, 1f))
val timeInk = themedColor(
day = ink(fill.day, SECONDARY_INK_ALPHA),
night = ink(fill.night, SECONDARY_INK_ALPHA),
day = widgetEventInk(fill.day, SECONDARY_INK_ALPHA),
night = widgetEventInk(fill.night, SECONDARY_INK_ALPHA),
)
Column(
modifier = GlanceModifier
@@ -1,5 +1,6 @@
package de.jeanlucmakiola.calendula.widget.countdown
import de.jeanlucmakiola.calendula.widget.InAppLanguage
import de.jeanlucmakiola.calendula.widget.WidgetKind
import de.jeanlucmakiola.calendula.widget.previewSizeOf
import de.jeanlucmakiola.calendula.widget.provideSamplePreview
@@ -49,9 +50,7 @@ import de.jeanlucmakiola.calendula.widget.formatWidgetTime
import de.jeanlucmakiola.calendula.widget.relativeDayLabel
import de.jeanlucmakiola.calendula.widget.systemZone
import de.jeanlucmakiola.calendula.widget.widgetAppearance
import de.jeanlucmakiola.calendula.widget.widgetCardColor
import de.jeanlucmakiola.floret.glance.LocalWidgetBackground
import de.jeanlucmakiola.floret.glance.WidgetBackground
import de.jeanlucmakiola.floret.glance.wallpaperBacking
import de.jeanlucmakiola.floret.glance.WidgetData
import de.jeanlucmakiola.floret.glance.WidgetMessage
import de.jeanlucmakiola.floret.glance.currentRefreshStamp
@@ -82,7 +81,9 @@ class CountdownWidget : GlanceAppWidget() {
val widgetData = WidgetData(context.loadPlacedCountdown(id), refreshStamp(context, id))
provideContent {
val loaded = widgetData.current(currentRefreshStamp()) { context.loadPlacedCountdown(id) }
CountdownWidgetContent(loaded.content, currentState<Preferences>().widgetAppearance, loaded.appTheme)
InAppLanguage {
CountdownWidgetContent(loaded.content, currentState<Preferences>().widgetAppearance, loaded.appTheme)
}
}
}
}
@@ -144,11 +145,7 @@ private fun EventCountdown(event: EventInstance, data: CountdownData.Ready) {
),
)
// Over a wallpaper the text sits on its own card, as every other widget's rows do.
val card = if (LocalWidgetBackground.current == WidgetBackground.TRANSPARENT) {
GlanceModifier.cornerRadius(16.dp).background(widgetCardColor())
} else {
GlanceModifier
}
val card = GlanceModifier.wallpaperBacking(radius = 16.dp)
Box(modifier = GlanceModifier.fillMaxSize().padding(6.dp)) {
Row(
modifier = card.fillMaxSize().clickable(open).padding(horizontal = 10.dp, vertical = 8.dp),
@@ -1,5 +1,6 @@
package de.jeanlucmakiola.calendula.widget.date
import de.jeanlucmakiola.calendula.widget.InAppLanguage
import de.jeanlucmakiola.calendula.widget.WidgetKind
import de.jeanlucmakiola.calendula.widget.previewSizeOf
import de.jeanlucmakiola.calendula.widget.provideSamplePreview
@@ -24,7 +25,6 @@ import androidx.glance.layout.Column
import androidx.glance.layout.Spacer
import androidx.glance.layout.fillMaxSize
import androidx.glance.layout.height
import androidx.glance.layout.padding
import androidx.glance.semantics.contentDescription
import androidx.glance.semantics.semantics
import androidx.glance.state.PreferencesGlanceStateDefinition
@@ -40,8 +40,6 @@ import de.jeanlucmakiola.calendula.widget.WidgetAppearance
import de.jeanlucmakiola.calendula.widget.loadToday
import de.jeanlucmakiola.calendula.widget.widgetAppearance
import de.jeanlucmakiola.calendula.widget.widgetLocale
import de.jeanlucmakiola.floret.glance.LocalWidgetBackground
import de.jeanlucmakiola.floret.glance.WidgetBackground
import de.jeanlucmakiola.floret.glance.WidgetData
import de.jeanlucmakiola.floret.glance.WidgetShape
import de.jeanlucmakiola.floret.glance.WidgetShapedBox
@@ -71,7 +69,9 @@ class DateWidget : GlanceAppWidget() {
val widgetData = WidgetData(context.loadToday(), refreshStamp(context, id))
provideContent {
val loaded = widgetData.current(currentRefreshStamp()) { context.loadToday() }
DateWidgetContent(loaded.content, currentState<Preferences>().widgetAppearance, loaded.appTheme)
InAppLanguage {
DateWidgetContent(loaded.content, currentState<Preferences>().widgetAppearance, loaded.appTheme)
}
}
}
}
@@ -105,11 +105,7 @@ internal fun DateWidgetContent(today: LocalDate, appearance: WidgetAppearance, a
verticalAlignment = Alignment.CenterVertically,
) {
val name = today.dayOfWeek.toJavaDayOfWeek().getDisplayName(JavaTextStyle.SHORT, widgetLocale())
val backing = if (LocalWidgetBackground.current == WidgetBackground.TRANSPARENT) {
GlanceModifier.wallpaperBacking(radius = 8.dp).padding(horizontal = 6.dp)
} else {
GlanceModifier
}
val backing = GlanceModifier.wallpaperBacking(radius = 8.dp, horizontalPadding = 6.dp)
Text(text = name.uppercase(widgetLocale()), maxLines = 1, style = weekday, modifier = backing)
Spacer(GlanceModifier.height(2.dp))
WidgetShapedBox(shape = WidgetShape.COOKIE, color = GlanceTheme.colors.primary, size = side) {
@@ -147,9 +147,9 @@ private fun openDayIntent(context: Context, date: LocalDate): PendingIntent = Pe
)
private val STRIP_IDS = intArrayOf(R.id.strip_0, R.id.strip_1, R.id.strip_2, R.id.strip_3, R.id.strip_4, R.id.strip_5, R.id.strip_6)
/** The shade over a day outside the month: a dark cell needs more of it to read as dimmed. */
private val OUTSIDE_SHADE_DAY = Color.Black.copy(alpha = 0.18f)
private val OUTSIDE_SHADE_NIGHT = Color.Black.copy(alpha = 0.50f)
/** The shade over a day outside the month: a dark cell needs more of it to read as dimmed. Mirrored by `widget_preview_outside_shade`. */
private val OUTSIDE_SHADE_DAY = Color.Black.copy(alpha = 0.11f)
private val OUTSIDE_SHADE_NIGHT = Color.Black.copy(alpha = 0.30f)
private val CELL_DIM_IDS = intArrayOf(
R.id.cell_dim_0, R.id.cell_dim_1, R.id.cell_dim_2, R.id.cell_dim_3, R.id.cell_dim_4, R.id.cell_dim_5, R.id.cell_dim_6,
@@ -1,5 +1,6 @@
package de.jeanlucmakiola.calendula.widget.month
import de.jeanlucmakiola.calendula.widget.InAppLanguage
import de.jeanlucmakiola.calendula.widget.WidgetKind
import de.jeanlucmakiola.calendula.widget.previewSizeOf
import de.jeanlucmakiola.calendula.widget.provideSamplePreview
@@ -169,12 +170,14 @@ class MonthWidget : GlanceAppWidget() {
else -> index.coerceIn(source.months)
}
SideEffect { lastShown = shown }
MonthWidgetContent(
source = source,
index = shown,
appearance = currentState<Preferences>().widgetAppearance,
appTheme = loaded.appTheme,
)
InAppLanguage {
MonthWidgetContent(
source = source,
index = shown,
appearance = currentState<Preferences>().widgetAppearance,
appTheme = loaded.appTheme,
)
}
}
}
}
@@ -1,5 +1,6 @@
package de.jeanlucmakiola.calendula.widget.quickadd
import de.jeanlucmakiola.calendula.widget.InAppLanguage
import de.jeanlucmakiola.calendula.widget.WidgetKind
import de.jeanlucmakiola.calendula.widget.previewSizeOf
import de.jeanlucmakiola.calendula.widget.provideSamplePreview
@@ -59,7 +60,9 @@ class QuickAddWidget : GlanceAppWidget() {
provideContent {
// The rollover moves the stamp, so the editor opens on the new day.
val loaded = widgetData.current(currentRefreshStamp()) { context.loadToday() }
QuickAddWidgetContent(loaded.content, currentState<Preferences>().widgetAppearance, loaded.appTheme)
InAppLanguage {
QuickAddWidgetContent(loaded.content, currentState<Preferences>().widgetAppearance, loaded.appTheme)
}
}
}
}
@@ -1,5 +1,6 @@
package de.jeanlucmakiola.calendula.widget.split
import de.jeanlucmakiola.calendula.widget.InAppLanguage
import de.jeanlucmakiola.calendula.widget.WidgetKind
import de.jeanlucmakiola.calendula.widget.previewSizeOf
import de.jeanlucmakiola.calendula.widget.provideSamplePreview
@@ -84,9 +85,7 @@ import kotlinx.datetime.toJavaLocalDate
import de.jeanlucmakiola.calendula.widget.widgetTimeLabel
import de.jeanlucmakiola.calendula.data.prefs.is24Hour
import de.jeanlucmakiola.floret.components.positionOf
import de.jeanlucmakiola.floret.glance.LocalWidgetBackground
import de.jeanlucmakiola.floret.glance.WidgetActionStyle
import de.jeanlucmakiola.floret.glance.WidgetBackground
import de.jeanlucmakiola.floret.glance.WidgetData
import de.jeanlucmakiola.floret.glance.WidgetHeader
import de.jeanlucmakiola.floret.glance.WidgetHeaderAction
@@ -168,7 +167,9 @@ class MonthDayWidget : GlanceAppWidget() {
val currentStamp = currentRefreshStamp()
val loaded = widgetData.current(currentCenter, currentStamp) { load(currentCenter, currentStamp) }
val selected = currentState(SELECTED_KEY)?.let(LocalDate::parse)
MonthDayWidgetContent(loaded.content, index, selected, currentState<Preferences>().widgetAppearance, loaded.appTheme)
InAppLanguage {
MonthDayWidgetContent(loaded.content, index, selected, currentState<Preferences>().widgetAppearance, loaded.appTheme)
}
}
}
}
@@ -331,11 +332,7 @@ private fun Grid(
) {
val locale = widgetLocale()
val month = index % 12 + 1
val backing = if (LocalWidgetBackground.current == WidgetBackground.TRANSPARENT) {
GlanceModifier.cornerRadius(14.dp).background(widgetCardColor())
} else {
GlanceModifier
}
val backing = GlanceModifier.wallpaperBacking(radius = 14.dp)
Column(modifier = backing.fillMaxSize().padding(2.dp)) {
Row(modifier = GlanceModifier.fillMaxWidth()) {
for (i in 0 until 7) {
@@ -1,5 +1,6 @@
package de.jeanlucmakiola.calendula.widget.today
import de.jeanlucmakiola.calendula.widget.InAppLanguage
import de.jeanlucmakiola.calendula.widget.WidgetKind
import de.jeanlucmakiola.calendula.widget.previewSizeOf
import de.jeanlucmakiola.calendula.widget.provideSamplePreview
@@ -62,8 +63,6 @@ import de.jeanlucmakiola.calendula.widget.widgetLocale
import de.jeanlucmakiola.calendula.widget.widgetLoadContext
import de.jeanlucmakiola.calendula.widget.widgetTimeLabel
import de.jeanlucmakiola.floret.components.positionOf
import de.jeanlucmakiola.floret.glance.LocalWidgetBackground
import de.jeanlucmakiola.floret.glance.WidgetBackground
import de.jeanlucmakiola.floret.glance.WidgetData
import de.jeanlucmakiola.floret.glance.WidgetMessage
import de.jeanlucmakiola.floret.glance.currentRefreshStamp
@@ -99,7 +98,9 @@ class TodayWidget : GlanceAppWidget() {
val widgetData = WidgetData(context.loadTodayList(context.widgetLoadContext(id)), refreshStamp(context, id))
provideContent {
val loaded = widgetData.current(currentRefreshStamp()) { context.loadTodayList(context.widgetLoadContext(id)) }
TodayWidgetContent(loaded.content, currentState<Preferences>().widgetAppearance, loaded.appTheme)
InAppLanguage {
TodayWidgetContent(loaded.content, currentState<Preferences>().widgetAppearance, loaded.appTheme)
}
}
}
}
@@ -189,11 +190,7 @@ private fun NothingLeft(tomorrow: List<EventInstance>, data: DaysWidgetData.Read
val room = LocalSize.current.height - 12.dp - lineHeight
val fits = ((room + RowGap) / (metrics.rowHeight + RowGap)).toInt().coerceIn(0, 2)
val ahead = tomorrow.take(fits)
val backing = if (LocalWidgetBackground.current == WidgetBackground.TRANSPARENT) {
GlanceModifier.wallpaperBacking(radius = 8.dp).padding(horizontal = 8.dp)
} else {
GlanceModifier.padding(horizontal = 4.dp)
}
val backing = GlanceModifier.wallpaperBacking(radius = 8.dp, horizontalPadding = 8.dp, filledHorizontalPadding = 4.dp)
Column(modifier = GlanceModifier.fillMaxSize(), verticalAlignment = Alignment.CenterVertically) {
Text(
text = context.getString(R.string.widget_nothing_left_today),
@@ -1,5 +1,6 @@
package de.jeanlucmakiola.calendula.widget.upcoming
import de.jeanlucmakiola.calendula.widget.InAppLanguage
import de.jeanlucmakiola.calendula.widget.WidgetKind
import de.jeanlucmakiola.calendula.widget.previewSizeOf
import de.jeanlucmakiola.calendula.widget.provideSamplePreview
@@ -54,8 +55,6 @@ import de.jeanlucmakiola.calendula.widget.widgetLocale
import de.jeanlucmakiola.calendula.widget.widgetLoadContext
import de.jeanlucmakiola.calendula.widget.widgetTimeLabel
import de.jeanlucmakiola.floret.components.positionOf
import de.jeanlucmakiola.floret.glance.LocalWidgetBackground
import de.jeanlucmakiola.floret.glance.WidgetBackground
import de.jeanlucmakiola.floret.glance.WidgetData
import de.jeanlucmakiola.floret.glance.WidgetMessage
import de.jeanlucmakiola.floret.glance.currentRefreshStamp
@@ -89,7 +88,9 @@ class UpcomingWidget : GlanceAppWidget() {
val widgetData = WidgetData(context.loadUpcoming(context.widgetLoadContext(id)), refreshStamp(context, id))
provideContent {
val loaded = widgetData.current(currentRefreshStamp()) { context.loadUpcoming(context.widgetLoadContext(id)) }
UpcomingWidgetContent(loaded.content, currentState<Preferences>().widgetAppearance, loaded.appTheme)
InAppLanguage {
UpcomingWidgetContent(loaded.content, currentState<Preferences>().widgetAppearance, loaded.appTheme)
}
}
}
}
@@ -155,11 +156,7 @@ internal fun UpcomingWidgetContent(data: DaysWidgetData, appearance: WidgetAppea
private fun DateHeader(today: LocalDate, height: Dp) {
val context = LocalContext.current
val locale = widgetLocale()
val backing = if (LocalWidgetBackground.current == WidgetBackground.TRANSPARENT) {
GlanceModifier.wallpaperBacking(radius = 12.dp).padding(horizontal = 8.dp)
} else {
GlanceModifier.padding(horizontal = 4.dp)
}
val backing = GlanceModifier.wallpaperBacking(radius = 12.dp, horizontalPadding = 8.dp, filledHorizontalPadding = 4.dp)
Box(modifier = GlanceModifier.fillMaxWidth().height(height), contentAlignment = Alignment.CenterStart) {
Row(
modifier = backing.clickable(actionStartActivity(MainActivity.openViewIntent(context, CalendarView.Agenda))),
@@ -1,5 +1,6 @@
package de.jeanlucmakiola.calendula.widget.week
import de.jeanlucmakiola.calendula.widget.InAppLanguage
import de.jeanlucmakiola.calendula.widget.WidgetKind
import de.jeanlucmakiola.calendula.widget.previewSizeOf
import de.jeanlucmakiola.calendula.widget.provideSamplePreview
@@ -91,7 +92,9 @@ class WeekStripWidget : GlanceAppWidget() {
val widgetData = WidgetData(context.loadWeek(id), refreshStamp(context, id))
provideContent {
val loaded = widgetData.current(currentRefreshStamp()) { context.loadWeek(id) }
WeekStripWidgetContent(loaded.content, currentState<Preferences>().widgetAppearance, loaded.appTheme)
InAppLanguage {
WeekStripWidgetContent(loaded.content, currentState<Preferences>().widgetAppearance, loaded.appTheme)
}
}
}
}
@@ -1,6 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/widget_preview_outside" />
<corners android:radius="12dp" />
</shape>
<!-- A day outside the month: its cell under the widget's shade (MonthGridViews). -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/preview_widget_cell_bg" />
<item>
<shape android:shape="rectangle">
<solid android:color="@color/widget_preview_outside_shade" />
<corners android:radius="12dp" />
</shape>
</item>
</layer-list>
@@ -9,9 +9,9 @@
android:gravity="center_vertical"
android:orientation="horizontal"
android:padding="12dp"
tools:ignore="HardcodedText,NestedWeights,DisableBaselineAlignment,SmallSp,TooManyViews,UselessParent">
tools:ignore="HardcodedText,NestedWeights,DisableBaselineAlignment,SmallSp,TooManyViews,UselessLeaf,UselessParent">
<View
<FrameLayout
android:layout_width="4dp"
android:layout_height="match_parent"
android:background="@drawable/preview_stripe"
@@ -8,7 +8,7 @@
android:background="@drawable/preview_widget_bg"
android:orientation="horizontal"
android:padding="6dp"
tools:ignore="HardcodedText,NestedWeights,DisableBaselineAlignment,SmallSp,TooManyViews,UselessParent">
tools:ignore="HardcodedText,NestedWeights,DisableBaselineAlignment,SmallSp,TooManyViews,UselessLeaf,UselessParent">
<LinearLayout
style="@style/WidgetPreviewWeekDay"
@@ -32,19 +32,19 @@
android:layout_height="6dp"
android:gravity="center"
android:orientation="horizontal">
<View
<FrameLayout
android:layout_width="4dp"
android:layout_height="4dp"
android:layout_marginHorizontal="1dp"
android:background="@drawable/preview_today_circle"
android:backgroundTint="@color/widget_preview_event_blue" />
<View
<FrameLayout
android:layout_width="4dp"
android:layout_height="4dp"
android:layout_marginHorizontal="1dp"
android:background="@drawable/preview_today_circle"
android:backgroundTint="@color/widget_preview_event_green" />
<View
<FrameLayout
android:layout_width="4dp"
android:layout_height="4dp"
android:layout_marginHorizontal="1dp"
@@ -73,7 +73,7 @@
android:layout_height="6dp"
android:gravity="center"
android:orientation="horizontal">
<View
<FrameLayout
android:layout_width="4dp"
android:layout_height="4dp"
android:layout_marginHorizontal="1dp"
@@ -102,7 +102,7 @@
android:layout_height="6dp"
android:gravity="center"
android:orientation="horizontal">
<View
<FrameLayout
android:layout_width="4dp"
android:layout_height="4dp"
android:layout_marginHorizontal="1dp"
@@ -126,7 +126,7 @@
android:textColor="@color/widget_preview_on_surface"
android:textSize="16sp"
android:textStyle="bold" />
<Space
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="6dp" />
</LinearLayout>
@@ -146,7 +146,7 @@
android:textColor="@color/widget_preview_on_surface"
android:textSize="16sp"
android:textStyle="bold" />
<Space
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="6dp" />
</LinearLayout>
@@ -171,7 +171,7 @@
android:layout_height="6dp"
android:gravity="center"
android:orientation="horizontal">
<View
<FrameLayout
android:layout_width="4dp"
android:layout_height="4dp"
android:layout_marginHorizontal="1dp"
@@ -195,7 +195,7 @@
android:textColor="@color/widget_preview_on_surface"
android:textSize="16sp"
android:textStyle="bold" />
<Space
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="6dp" />
</LinearLayout>
@@ -2,7 +2,6 @@
<resources>
<color name="widget_preview_surface">@android:color/system_accent2_700</color>
<color name="widget_preview_cell">@android:color/system_accent2_900</color>
<color name="widget_preview_outside">@android:color/system_neutral2_600</color>
<color name="widget_preview_on_surface">@android:color/system_neutral1_50</color>
<color name="widget_preview_variant">@android:color/system_neutral2_200</color>
<color name="widget_preview_primary">@android:color/system_accent1_200</color>
@@ -2,7 +2,7 @@
<resources>
<color name="widget_preview_surface">#2B3742</color>
<color name="widget_preview_cell">#1C252D</color>
<color name="widget_preview_outside">#3A4651</color>
<color name="widget_preview_outside_shade">#4D000000</color>
<color name="widget_preview_on_surface">#E1E3E6</color>
<color name="widget_preview_variant">#A8ADB2</color>
<color name="widget_preview_primary">#A3CBE2</color>
@@ -2,7 +2,6 @@
<resources>
<color name="widget_preview_surface">@android:color/system_accent2_100</color>
<color name="widget_preview_cell">@android:color/system_accent2_10</color>
<color name="widget_preview_outside">@android:color/system_neutral2_300</color>
<color name="widget_preview_on_surface">@android:color/system_neutral1_900</color>
<color name="widget_preview_variant">@android:color/system_neutral2_700</color>
<color name="widget_preview_primary">@android:color/system_accent1_600</color>
+18 -9
View File
@@ -433,21 +433,29 @@
<!-- Home-screen widgets -->
<string name="widget_agenda_title">Upcoming</string>
<string name="widget_agenda_label">Calendula agenda</string>
<string name="widget_month_label">Calendula month</string>
<string name="widget_quick_add_label">Calendula quick add</string>
<string name="widget_date_label">Calendula date</string>
<string name="widget_week_strip_label">Calendula week strip</string>
<string name="widget_agenda_label">Agenda</string>
<string name="widget_agenda_description">Your next events, grouped by day</string>
<string name="widget_month_label">Month</string>
<string name="widget_month_description">The month at a glance, with your events</string>
<string name="widget_quick_add_label">Quick add</string>
<string name="widget_quick_add_description">Start a new event in one tap</string>
<string name="widget_date_label">Date</string>
<string name="widget_date_description">Today\'s date</string>
<string name="widget_week_strip_label">Week strip</string>
<string name="widget_week_strip_description">This week, with the days that have events</string>
<!-- A day in the week strip widget, for screen readers: "Wednesday, 7 October, 2 events" -->
<plurals name="widget_day_with_events">
<item quantity="one">%1$s, %2$d event</item>
<item quantity="other">%1$s, %2$d events</item>
</plurals>
<string name="widget_upcoming_label">Calendula upcoming</string>
<string name="widget_upcoming_label">Upcoming</string>
<string name="widget_upcoming_description">Today\'s date over your next events</string>
<string name="widget_upcoming_empty">No upcoming events</string>
<string name="widget_today_label">Calendula today</string>
<string name="widget_today_label">Today</string>
<string name="widget_today_description">Today\'s events, then tomorrow\'s</string>
<string name="widget_nothing_left_today">Nothing left today</string>
<string name="widget_countdown_label">Calendula countdown</string>
<string name="widget_countdown_label">Countdown</string>
<string name="widget_countdown_description">Time until an event, or the next one</string>
<string name="widget_countdown_now">Now</string>
<string name="widget_countdown_ending_now">Ending now</string>
<string name="widget_countdown_tomorrow">Tomorrow</string>
@@ -492,7 +500,8 @@
<string name="widget_config_countdown_load_failed">Couldn\'t load your events.</string>
<!-- A repeating event in the countdown's event list: "Next: Fri, 24 Oct · repeats" -->
<string name="widget_config_countdown_repeats">%1$s · repeats</string>
<string name="widget_month_day_label">Calendula month and day</string>
<string name="widget_month_day_label">Month and day</string>
<string name="widget_month_day_description">The month and the events of the day you pick</string>
<string name="widget_day_empty">No events</string>
<string name="widget_refresh">Refresh</string>
<string name="widget_new_event">New event</string>
@@ -6,7 +6,7 @@
<resources>
<color name="widget_preview_surface">#D6E4F0</color>
<color name="widget_preview_cell">#EEF3F8</color>
<color name="widget_preview_outside">#C2CCD6</color>
<color name="widget_preview_outside_shade">#1C000000</color>
<color name="widget_preview_on_surface">#191C1F</color>
<color name="widget_preview_variant">#6E7479</color>
<color name="widget_preview_primary">#3B5364</color>
@@ -10,7 +10,7 @@
android:widgetCategory="home_screen"
android:configure="de.jeanlucmakiola.calendula.widget.config.WidgetConfigActivity"
android:widgetFeatures="reconfigurable"
android:description="@string/widget_agenda_label"
android:description="@string/widget_agenda_description"
android:updatePeriodMillis="1800000"
android:previewLayout="@layout/widget_preview_agenda"
android:initialLayout="@layout/glance_default_loading_layout" />
@@ -10,7 +10,7 @@
android:widgetCategory="home_screen"
android:configure="de.jeanlucmakiola.calendula.widget.config.WidgetConfigActivity"
android:widgetFeatures="reconfigurable"
android:description="@string/widget_countdown_label"
android:description="@string/widget_countdown_description"
android:updatePeriodMillis="1800000"
android:previewLayout="@layout/widget_preview_countdown"
android:initialLayout="@layout/glance_default_loading_layout" />
+1 -1
View File
@@ -8,7 +8,7 @@
android:widgetCategory="home_screen"
android:configure="de.jeanlucmakiola.calendula.widget.config.WidgetConfigActivity"
android:widgetFeatures="reconfigurable"
android:description="@string/widget_date_label"
android:description="@string/widget_date_description"
android:updatePeriodMillis="1800000"
android:previewLayout="@layout/widget_preview_date"
android:initialLayout="@layout/glance_default_loading_layout" />
@@ -10,7 +10,7 @@
android:widgetCategory="home_screen"
android:configure="de.jeanlucmakiola.calendula.widget.config.WidgetConfigActivity"
android:widgetFeatures="reconfigurable"
android:description="@string/widget_month_label"
android:description="@string/widget_month_description"
android:updatePeriodMillis="1800000"
android:previewLayout="@layout/widget_preview_month"
android:initialLayout="@layout/glance_default_loading_layout" />
@@ -10,7 +10,7 @@
android:widgetCategory="home_screen"
android:configure="de.jeanlucmakiola.calendula.widget.config.WidgetConfigActivity"
android:widgetFeatures="reconfigurable"
android:description="@string/widget_month_day_label"
android:description="@string/widget_month_day_description"
android:updatePeriodMillis="1800000"
android:previewLayout="@layout/widget_preview_month_day"
android:initialLayout="@layout/glance_default_loading_layout" />
@@ -8,7 +8,7 @@
android:widgetCategory="home_screen"
android:configure="de.jeanlucmakiola.calendula.widget.config.WidgetConfigActivity"
android:widgetFeatures="reconfigurable"
android:description="@string/widget_quick_add_label"
android:description="@string/widget_quick_add_description"
android:updatePeriodMillis="1800000"
android:previewLayout="@layout/widget_preview_quick_add"
android:initialLayout="@layout/glance_default_loading_layout" />
@@ -12,7 +12,7 @@
android:widgetCategory="home_screen"
android:configure="de.jeanlucmakiola.calendula.widget.config.WidgetConfigActivity"
android:widgetFeatures="reconfigurable"
android:description="@string/widget_today_label"
android:description="@string/widget_today_description"
android:updatePeriodMillis="1800000"
android:previewLayout="@layout/widget_preview_today"
android:initialLayout="@layout/glance_default_loading_layout" />
@@ -10,7 +10,7 @@
android:widgetCategory="home_screen"
android:configure="de.jeanlucmakiola.calendula.widget.config.WidgetConfigActivity"
android:widgetFeatures="reconfigurable"
android:description="@string/widget_upcoming_label"
android:description="@string/widget_upcoming_description"
android:updatePeriodMillis="1800000"
android:previewLayout="@layout/widget_preview_upcoming"
android:initialLayout="@layout/glance_default_loading_layout" />
@@ -10,7 +10,7 @@
android:widgetCategory="home_screen"
android:configure="de.jeanlucmakiola.calendula.widget.config.WidgetConfigActivity"
android:widgetFeatures="reconfigurable"
android:description="@string/widget_week_strip_label"
android:description="@string/widget_week_strip_description"
android:updatePeriodMillis="1800000"
android:previewLayout="@layout/widget_preview_week_strip"
android:initialLayout="@layout/glance_default_loading_layout" />