diff --git a/app/src/main/java/de/jeanlucmakiola/calendula/data/prefs/SettingsPrefs.kt b/app/src/main/java/de/jeanlucmakiola/calendula/data/prefs/SettingsPrefs.kt index 99f1334..1cb6020 100644 --- a/app/src/main/java/de/jeanlucmakiola/calendula/data/prefs/SettingsPrefs.kt +++ b/app/src/main/java/de/jeanlucmakiola/calendula/data/prefs/SettingsPrefs.kt @@ -137,6 +137,24 @@ class SettingsPrefs @Inject constructor( store.edit { it[AGENDA_WIDGET_RANGE_KEY] = range.storageValue() } } + /** Whether the agenda shows its range-header banner (v2.11). Default ON. */ + val agendaShowRangeBanner: Flow = store.data.map { prefs -> + prefs[AGENDA_SHOW_RANGE_BANNER_KEY] ?: true + } + + suspend fun setAgendaShowRangeBanner(enabled: Boolean) { + store.edit { it[AGENDA_SHOW_RANGE_BANNER_KEY] = enabled } + } + + /** Whether the agenda shows the bottom-left range pill (v2.11). Default ON. */ + val agendaShowRangePill: Flow = store.data.map { prefs -> + prefs[AGENDA_SHOW_RANGE_PILL_KEY] ?: true + } + + suspend fun setAgendaShowRangePill(enabled: Boolean) { + store.edit { it[AGENDA_SHOW_RANGE_PILL_KEY] = enabled } + } + /** * The calendar view the app opens on (M1). Defaults to [CalendarView.Week] — * the historical hard-coded startup view — so existing users see no change @@ -328,6 +346,8 @@ class SettingsPrefs @Inject constructor( internal val WEEK_START_KEY = stringPreferencesKey("week_start") internal val AGENDA_SCREEN_RANGE_KEY = stringPreferencesKey("agenda_screen_range") internal val AGENDA_WIDGET_RANGE_KEY = stringPreferencesKey("agenda_widget_range") + internal val AGENDA_SHOW_RANGE_BANNER_KEY = booleanPreferencesKey("agenda_show_range_banner") + internal val AGENDA_SHOW_RANGE_PILL_KEY = booleanPreferencesKey("agenda_show_range_pill") internal val TIME_FORMAT_KEY = stringPreferencesKey("time_format") internal val SHOW_HOUR_LINES_KEY = booleanPreferencesKey("show_hour_lines") internal val DEFAULT_VIEW_KEY = stringPreferencesKey("default_view") diff --git a/app/src/main/java/de/jeanlucmakiola/calendula/ui/agenda/AgendaScreen.kt b/app/src/main/java/de/jeanlucmakiola/calendula/ui/agenda/AgendaScreen.kt index 4b76191..9f28a22 100644 --- a/app/src/main/java/de/jeanlucmakiola/calendula/ui/agenda/AgendaScreen.kt +++ b/app/src/main/java/de/jeanlucmakiola/calendula/ui/agenda/AgendaScreen.kt @@ -150,17 +150,27 @@ fun AgendaScreen( }, ) { innerPadding -> Box(modifier = Modifier.fillMaxSize()) { - AgendaContent( - state = state, - onRetry = viewModel::goToToday, - onEventClick = onEventClick, + Column( modifier = Modifier - .padding(innerPadding) - .fillMaxSize(), - ) + .fillMaxSize() + .padding(innerPadding), + ) { + // A header naming the concrete window currently shown. + successState?.takeIf { it.showRangeBanner }?.let { s -> + AgendaRangeBanner(range = s.range, start = s.anchor, end = s.rangeEnd) + } + AgendaContent( + state = state, + onRetry = viewModel::goToToday, + onEventClick = onEventClick, + modifier = Modifier + .weight(1f) + .fillMaxWidth(), + ) + } // A bottom-left pill to peek at a different range for this // session, mirroring the FAB column on the right. - successState?.let { s -> + successState?.takeIf { it.showRangePill }?.let { s -> AgendaRangePill( range = s.range, isOverride = s.rangeIsOverride, @@ -176,16 +186,12 @@ fun AgendaScreen( } if (showRangePicker) { - val locale = currentLocale() AgendaRangePicker( title = stringResource(R.string.settings_agenda_range), description = stringResource(R.string.agenda_range_override_hint), selected = successState?.range ?: AgendaRange.Month, onSelect = viewModel::setRangeOverride, onDismiss = { showRangePicker = false }, - currentWindow = successState?.let { - agendaRangeWindowSummary(it.range, it.anchor, it.rangeEnd, locale) - }, ) } } @@ -236,6 +242,33 @@ private fun AgendaRangePill( } } +/** + * A header naming the concrete window currently shown, e.g. "Showing all events + * for · Today, 27 Jun 2026" / "This week, 27 Jun – 3 Jul" / "This month, June 2026". + */ +@Composable +private fun AgendaRangeBanner( + range: AgendaRange, + start: LocalDate, + end: LocalDate, + modifier: Modifier = Modifier, +) { + val locale = currentLocale() + val window = agendaRangeWindowSummary(range, start, end, locale) + Column(modifier = modifier.padding(horizontal = 28.dp, vertical = 8.dp)) { + Text( + text = stringResource(R.string.agenda_range_showing_label), + style = MaterialTheme.typography.labelMedium, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + Text( + text = "${agendaRangeLabel(range)}, $window", + style = MaterialTheme.typography.titleMedium, + color = MaterialTheme.colorScheme.onSurface, + ) + } +} + @Composable private fun AgendaContent( state: AgendaUiState, diff --git a/app/src/main/java/de/jeanlucmakiola/calendula/ui/agenda/AgendaUiState.kt b/app/src/main/java/de/jeanlucmakiola/calendula/ui/agenda/AgendaUiState.kt index 5101f75..0b8414c 100644 --- a/app/src/main/java/de/jeanlucmakiola/calendula/ui/agenda/AgendaUiState.kt +++ b/app/src/main/java/de/jeanlucmakiola/calendula/ui/agenda/AgendaUiState.kt @@ -59,5 +59,9 @@ sealed interface AgendaUiState { val rangeIsOverride: Boolean, /** Last day the current [range] covers (inclusive), for the range header. */ val rangeEnd: LocalDate, + /** Whether to show the range header banner (settings toggle, on by default). */ + val showRangeBanner: Boolean, + /** Whether to show the bottom-left range pill (settings toggle, on by default). */ + val showRangePill: Boolean, ) : AgendaUiState } diff --git a/app/src/main/java/de/jeanlucmakiola/calendula/ui/agenda/AgendaViewModel.kt b/app/src/main/java/de/jeanlucmakiola/calendula/ui/agenda/AgendaViewModel.kt index ae82858..758e296 100644 --- a/app/src/main/java/de/jeanlucmakiola/calendula/ui/agenda/AgendaViewModel.kt +++ b/app/src/main/java/de/jeanlucmakiola/calendula/ui/agenda/AgendaViewModel.kt @@ -43,7 +43,12 @@ class AgendaViewModel @Inject constructor( @IoDispatcher private val io: CoroutineDispatcher, ) : ViewModel() { - private val screenRange = settingsPrefs.agendaScreenRange + // The saved agenda range plus the banner/pill visibility toggles. + private val agendaSettings = combine( + settingsPrefs.agendaScreenRange, + settingsPrefs.agendaShowRangeBanner, + settingsPrefs.agendaShowRangePill, + ) { range, showBanner, showPill -> AgendaSettings(range, showBanner, showPill) } // First day of the week, for the calendar-aligned "this week" range. private val weekStartDay = settingsPrefs.weekStart @@ -63,12 +68,14 @@ class AgendaViewModel @Inject constructor( private val _rangeOverride = MutableStateFlow(null) val state: StateFlow = - combine(_anchor, screenRange, _rangeOverride, weekStartDay) { anchor, default, override, weekStart -> + combine(_anchor, agendaSettings, _rangeOverride, weekStartDay) { anchor, settings, override, weekStart -> AgendaParams( anchor = anchor, - range = override ?: default, - rangeIsOverride = override != null && override != default, + range = override ?: settings.range, + rangeIsOverride = override != null && override != settings.range, weekStart = weekStart, + showRangeBanner = settings.showBanner, + showRangePill = settings.showPill, ) } .flatMapLatest { params -> @@ -106,11 +113,19 @@ class AgendaViewModel @Inject constructor( _rangeOverride.value = range } + private data class AgendaSettings( + val range: AgendaRange, + val showBanner: Boolean, + val showPill: Boolean, + ) + private data class AgendaParams( val anchor: LocalDate, val range: AgendaRange, val rangeIsOverride: Boolean, val weekStart: DayOfWeek, + val showRangeBanner: Boolean, + val showRangePill: Boolean, ) private fun buildState( @@ -134,6 +149,8 @@ class AgendaViewModel @Inject constructor( range = params.range, rangeIsOverride = params.rangeIsOverride, rangeEnd = rangeEnd, + showRangeBanner = params.showRangeBanner, + showRangePill = params.showRangePill, ) } } diff --git a/app/src/main/java/de/jeanlucmakiola/calendula/ui/common/Picker.kt b/app/src/main/java/de/jeanlucmakiola/calendula/ui/common/Picker.kt index e1f8d19..05e7ba8 100644 --- a/app/src/main/java/de/jeanlucmakiola/calendula/ui/common/Picker.kt +++ b/app/src/main/java/de/jeanlucmakiola/calendula/ui/common/Picker.kt @@ -287,7 +287,6 @@ private fun SelectedCheck() { * Agenda-range picker, full-screen. Two grouped lists — the calendar-aligned * options (today / this week / this month) and the rolling windows (next 7 / 30 * days), with a "Custom" row that expands an inline day-count editor (1–365). - * When [currentWindow] is given, a header shows the concrete span in effect. * Mirrors [ReminderDefaultPicker]'s custom-expand pattern. */ @Composable @@ -297,7 +296,6 @@ fun AgendaRangePicker( selected: AgendaRange, onSelect: (AgendaRange) -> Unit, onDismiss: () -> Unit, - currentWindow: String? = null, ) { val calendarAligned = listOf(AgendaRange.Day, AgendaRange.ThisWeek, AgendaRange.ThisMonth) val rolling = listOf(AgendaRange.Week, AgendaRange.Month) @@ -328,7 +326,6 @@ fun AgendaRangePicker( } FullScreenPicker(title = title, onDismiss = onDismiss) { - if (currentWindow != null) AgendaRangeHeader(currentWindow) PickerDescription(description) // Calendar-aligned windows. @@ -374,23 +371,6 @@ fun AgendaRangePicker( } } -/** The "Showing all events for " header at the top of the range picker. */ -@Composable -private fun AgendaRangeHeader(window: String) { - Column(modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp)) { - Text( - text = stringResource(R.string.agenda_range_showing_label), - style = MaterialTheme.typography.labelMedium, - color = MaterialTheme.colorScheme.onSurfaceVariant, - ) - Text( - text = window, - style = MaterialTheme.typography.titleMedium, - color = MaterialTheme.colorScheme.onSurface, - ) - } -} - /** The expanded "Custom" day-count editor: an amount field (1–365) and Set. */ @Composable private fun CustomDaysEditor( diff --git a/app/src/main/java/de/jeanlucmakiola/calendula/ui/settings/SettingsScreen.kt b/app/src/main/java/de/jeanlucmakiola/calendula/ui/settings/SettingsScreen.kt index 449602e..de031ed 100644 --- a/app/src/main/java/de/jeanlucmakiola/calendula/ui/settings/SettingsScreen.kt +++ b/app/src/main/java/de/jeanlucmakiola/calendula/ui/settings/SettingsScreen.kt @@ -514,9 +514,33 @@ private fun AppearanceScreen( GroupedRow( title = stringResource(R.string.settings_agenda_widget_range), summary = agendaRangeLabel(state.agendaWidgetRange), - position = Position.Bottom, + position = Position.Middle, onClick = { showAgendaWidgetRange = true }, ) + GroupedRow( + title = stringResource(R.string.settings_agenda_range_title), + summary = stringResource(R.string.settings_agenda_range_title_hint), + position = Position.Middle, + trailing = { + Switch( + checked = state.agendaShowRangeBanner, + onCheckedChange = viewModel::setAgendaShowRangeBanner, + ) + }, + onClick = { viewModel.setAgendaShowRangeBanner(!state.agendaShowRangeBanner) }, + ) + GroupedRow( + title = stringResource(R.string.settings_agenda_range_button), + summary = stringResource(R.string.settings_agenda_range_button_hint), + position = Position.Bottom, + trailing = { + Switch( + checked = state.agendaShowRangePill, + onCheckedChange = viewModel::setAgendaShowRangePill, + ) + }, + onClick = { viewModel.setAgendaShowRangePill(!state.agendaShowRangePill) }, + ) } if (showTheme) { diff --git a/app/src/main/java/de/jeanlucmakiola/calendula/ui/settings/SettingsUiState.kt b/app/src/main/java/de/jeanlucmakiola/calendula/ui/settings/SettingsUiState.kt index 3d7b657..fb1958e 100644 --- a/app/src/main/java/de/jeanlucmakiola/calendula/ui/settings/SettingsUiState.kt +++ b/app/src/main/java/de/jeanlucmakiola/calendula/ui/settings/SettingsUiState.kt @@ -28,6 +28,10 @@ data class SettingsUiState( val agendaScreenRange: AgendaRange = AgendaRange.Month, /** How far ahead the agenda widget shows events (v2.11). */ val agendaWidgetRange: AgendaRange = AgendaRange.Month, + /** Whether the agenda shows its range-header banner (v2.11). */ + val agendaShowRangeBanner: Boolean = true, + /** Whether the agenda shows the bottom-left range pill (v2.11). */ + val agendaShowRangePill: Boolean = true, /** The calendar view the app opens on, and the home of the view back stack (M1). */ val defaultView: CalendarView = CalendarView.Week, /** Optional event-form fields shown by default (rest behind "more fields"). */ diff --git a/app/src/main/java/de/jeanlucmakiola/calendula/ui/settings/SettingsViewModel.kt b/app/src/main/java/de/jeanlucmakiola/calendula/ui/settings/SettingsViewModel.kt index bcd7e3e..740b566 100644 --- a/app/src/main/java/de/jeanlucmakiola/calendula/ui/settings/SettingsViewModel.kt +++ b/app/src/main/java/de/jeanlucmakiola/calendula/ui/settings/SettingsViewModel.kt @@ -84,13 +84,19 @@ class SettingsViewModel @Inject constructor( ) { view, screenRange, widgetRange, timeFormat, showHourLines -> ViewSettings(view, screenRange, widgetRange, timeFormat, showHourLines) }, - ) { base, defaults, overrides, views -> + combine( + prefs.agendaShowRangeBanner, + prefs.agendaShowRangePill, + ) { banner, pill -> banner to pill }, + ) { base, defaults, overrides, views, agendaToggles -> base.copy( defaultView = views.defaultView, agendaScreenRange = views.agendaScreenRange, agendaWidgetRange = views.agendaWidgetRange, timeFormat = views.timeFormat, showHourLines = views.showHourLines, + agendaShowRangeBanner = agendaToggles.first, + agendaShowRangePill = agendaToggles.second, allowColorOnUnsupportedCalendars = defaults.allowColor, defaultReminderMinutes = defaults.defaultReminder, defaultAllDayReminderMinutes = defaults.allDayReminder, @@ -148,6 +154,14 @@ class SettingsViewModel @Inject constructor( viewModelScope.launch { prefs.setAgendaWidgetRange(range) } } + fun setAgendaShowRangeBanner(enabled: Boolean) { + viewModelScope.launch { prefs.setAgendaShowRangeBanner(enabled) } + } + + fun setAgendaShowRangePill(enabled: Boolean) { + viewModelScope.launch { prefs.setAgendaShowRangePill(enabled) } + } + fun setTimeFormat(pref: TimeFormatPref) { viewModelScope.launch { prefs.setTimeFormat(pref) } } diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 51d28c3..9a8e1aa 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -286,6 +286,10 @@ Wie weit im Voraus die Agenda-Ansicht Termine anzeigt. Agenda-Widget-Zeitraum Wie weit im Voraus das Agenda-Widget auf dem Startbildschirm Termine anzeigt. + Zeitraum-Titel + Eine Überschrift anzeigen, die den dargestellten Zeitraum benennt + Zeitraum-Schaltfläche + Eine Schaltfläche in der Agenda anzeigen, um den Zeitraum für die Sitzung zu wechseln Heute Diese Woche Dieser Monat diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 4cd9e56..3cbb111 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -279,6 +279,10 @@ How far ahead the Agenda screen lists events. Agenda widget range How far ahead the agenda home-screen widget lists events. + Range title + Show a header naming the dates the agenda is showing + Range button + Show a button in the agenda to switch the range for the session Today This week This month