From 8a807fcd621a1d9dc81f5e01a9be58030d808b2e Mon Sep 17 00:00:00 2001 From: Brad Pitcher Date: Fri, 26 Jun 2026 16:40:18 -0800 Subject: [PATCH] Add AM/PM style option for clock overlay (#7) - Added useAmPm to ConfigProvider and implemented it in JsonConfigService. - Added use_am_pm setting to assets/config.json. - Added localization strings for useAmPm in English and German. - Updated SettingsScreen to include a switch for AM/PM format under Clock settings. - Updated ClockOverlay to format time in 12-hour format when useAmPm is true. - Updated SlideshowScreen to pass the useAmPm setting to ClockOverlay. - Fixed missing concrete implementations of useAmPm in test mocks. Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> Co-authored-by: brad <1614+brad@users.noreply.github.com> --- assets/config.json | 4 ++++ lib/domain/interfaces/config_provider.dart | 3 +++ .../services/json_config_service.dart | 8 ++++++++ lib/l10n/app_de.arb | 2 ++ lib/l10n/app_en.arb | 2 ++ lib/l10n/app_localizations.dart | 12 ++++++++++++ lib/l10n/app_localizations_de.dart | 6 ++++++ lib/l10n/app_localizations_en.dart | 6 ++++++ lib/ui/screens/settings_screen.dart | 13 +++++++++++++ lib/ui/screens/slideshow_screen.dart | 3 ++- lib/ui/widgets/clock_overlay.dart | 11 ++++++++++- .../repositories/hybrid_photo_repository_test.dart | 3 +++ .../services/app_initializer_test.dart | 3 +++ .../photo_service_directory_change_test.dart | 3 +++ 14 files changed, 77 insertions(+), 2 deletions(-) diff --git a/assets/config.json b/assets/config.json index 2a575a9..495da3b 100644 --- a/assets/config.json +++ b/assets/config.json @@ -4,6 +4,10 @@ "blur_borders": true, "sync_interval_minutes": 15, "delete_orphaned_files": true, + "show_clock": true, + "clock_size": "large", + "clock_position": "bottomRight", + "use_am_pm": false, "active_source": "", "sources": { "nextcloud_link": { diff --git a/lib/domain/interfaces/config_provider.dart b/lib/domain/interfaces/config_provider.dart index 5bec632..a961b93 100644 --- a/lib/domain/interfaces/config_provider.dart +++ b/lib/domain/interfaces/config_provider.dart @@ -59,6 +59,9 @@ abstract class ConfigProvider extends ChangeNotifier { String get clockPosition; // 'bottomRight', 'bottomLeft', 'topRight', 'topLeft' set clockPosition(String value); + + bool get useAmPm; // Use 12h format with AM/PM + set useAmPm(bool value); // Display schedule settings (day/night mode) bool get scheduleEnabled; // Enable day/night schedule diff --git a/lib/infrastructure/services/json_config_service.dart b/lib/infrastructure/services/json_config_service.dart index 7b87361..396498b 100644 --- a/lib/infrastructure/services/json_config_service.dart +++ b/lib/infrastructure/services/json_config_service.dart @@ -383,6 +383,14 @@ class JsonConfigService extends ConfigProvider { set clockPosition(String value) { _config['clock_position'] = value; } + + @override + bool get useAmPm => _config['use_am_pm'] ?? false; + + @override + set useAmPm(bool value) { + _config['use_am_pm'] = value; + } // Display schedule settings (day/night mode) @override diff --git a/lib/l10n/app_de.arb b/lib/l10n/app_de.arb index 9e33cda..5d892fb 100644 --- a/lib/l10n/app_de.arb +++ b/lib/l10n/app_de.arb @@ -16,6 +16,8 @@ "showClockSubtitle": "Uhrzeit auf der Diashow anzeigen", "size": "Größe", "position": "Position", + "useAmPm": "AM/PM verwenden", + "useAmPmSubtitle": "Uhr im 12-Stunden-Format mit AM/PM anzeigen", "sectionPhotoInfo": "Foto-Informationen", "showPhotoInfo": "Foto-Info anzeigen", diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index e69db4a..42fc19f 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -16,6 +16,8 @@ "showClockSubtitle": "Display time on slideshow", "size": "Size", "position": "Position", + "useAmPm": "Use AM/PM", + "useAmPmSubtitle": "Display clock in 12-hour format with AM/PM", "sectionPhotoInfo": "Photo Information", "showPhotoInfo": "Show Photo Info", diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart index 8b5ecc7..438d6d8 100644 --- a/lib/l10n/app_localizations.dart +++ b/lib/l10n/app_localizations.dart @@ -176,6 +176,18 @@ abstract class AppLocalizations { /// **'Position'** String get position; + /// No description provided for @useAmPm. + /// + /// In en, this message translates to: + /// **'Use AM/PM'** + String get useAmPm; + + /// No description provided for @useAmPmSubtitle. + /// + /// In en, this message translates to: + /// **'Display clock in 12-hour format with AM/PM'** + String get useAmPmSubtitle; + /// No description provided for @sectionPhotoInfo. /// /// In en, this message translates to: diff --git a/lib/l10n/app_localizations_de.dart b/lib/l10n/app_localizations_de.dart index b63fdeb..7caa575 100644 --- a/lib/l10n/app_localizations_de.dart +++ b/lib/l10n/app_localizations_de.dart @@ -48,6 +48,12 @@ class AppLocalizationsDe extends AppLocalizations { @override String get position => 'Position'; + @override + String get useAmPm => 'AM/PM verwenden'; + + @override + String get useAmPmSubtitle => 'Uhr im 12-Stunden-Format mit AM/PM anzeigen'; + @override String get sectionPhotoInfo => 'Foto-Informationen'; diff --git a/lib/l10n/app_localizations_en.dart b/lib/l10n/app_localizations_en.dart index e617181..ad3919e 100644 --- a/lib/l10n/app_localizations_en.dart +++ b/lib/l10n/app_localizations_en.dart @@ -47,6 +47,12 @@ class AppLocalizationsEn extends AppLocalizations { @override String get position => 'Position'; + @override + String get useAmPm => 'Use AM/PM'; + + @override + String get useAmPmSubtitle => 'Display clock in 12-hour format with AM/PM'; + @override String get sectionPhotoInfo => 'Photo Information'; diff --git a/lib/ui/screens/settings_screen.dart b/lib/ui/screens/settings_screen.dart index 09e1a82..e8200fa 100644 --- a/lib/ui/screens/settings_screen.dart +++ b/lib/ui/screens/settings_screen.dart @@ -64,6 +64,7 @@ class _SettingsScreenState extends State with WidgetsBindingObse late bool _showClock; late String _clockSize; late String _clockPosition; + late bool _useAmPm; // Photo info settings late bool _showPhotoInfo; @@ -150,6 +151,7 @@ class _SettingsScreenState extends State with WidgetsBindingObse _showClock = config.showClock; _clockSize = config.clockSize; _clockPosition = config.clockPosition; + _useAmPm = config.useAmPm; // Photo info settings _showPhotoInfo = config.showPhotoInfo; @@ -336,6 +338,7 @@ class _SettingsScreenState extends State with WidgetsBindingObse config.showClock = _showClock; config.clockSize = _clockSize; config.clockPosition = _clockPosition; + config.useAmPm = _useAmPm; // Photo info settings config.showPhotoInfo = _showPhotoInfo; @@ -472,6 +475,16 @@ class _SettingsScreenState extends State with WidgetsBindingObse _buildClockSizeSelector(), const SizedBox(height: 8), _buildClockPositionSelector(), + const SizedBox(height: 16), + SwitchListTile( + title: Text(AppLocalizations.of(context)!.useAmPm), + subtitle: Text(AppLocalizations.of(context)!.useAmPmSubtitle), + secondary: const Icon(Icons.more_time), + value: _useAmPm, + onChanged: (value) { + setState(() => _useAmPm = value); + }, + ), ], const SizedBox(height: 24), diff --git a/lib/ui/screens/slideshow_screen.dart b/lib/ui/screens/slideshow_screen.dart index 2d84a2f..909d9ec 100644 --- a/lib/ui/screens/slideshow_screen.dart +++ b/lib/ui/screens/slideshow_screen.dart @@ -727,9 +727,10 @@ class _SlideshowScreenState extends State with TickerProviderSt // 2. Clock Overlay if (config.showClock) ClockOverlay( - key: ValueKey('clock_${config.clockSize}_${config.clockPosition}'), + key: ValueKey('clock_${config.clockSize}_${config.clockPosition}_${config.useAmPm}'), size: config.clockSize, position: config.clockPosition, + useAmPm: config.useAmPm, ), // 3. Photo Info Overlay diff --git a/lib/ui/widgets/clock_overlay.dart b/lib/ui/widgets/clock_overlay.dart index e77aa99..61289c9 100644 --- a/lib/ui/widgets/clock_overlay.dart +++ b/lib/ui/widgets/clock_overlay.dart @@ -5,11 +5,13 @@ import 'package:flutter/material.dart'; class ClockOverlay extends StatefulWidget { final String size; // 'small', 'medium', 'large' final String position; // 'bottomRight', 'bottomLeft', 'topRight', 'topLeft' + final bool useAmPm; const ClockOverlay({ super.key, required this.size, required this.position, + required this.useAmPm, }); @override @@ -78,7 +80,14 @@ class _ClockOverlayState extends State { @override Widget build(BuildContext context) { - final timeString = '${_now.hour.toString().padLeft(2, '0')}:${_now.minute.toString().padLeft(2, '0')}'; + String timeString; + if (widget.useAmPm) { + final hour = _now.hour % 12 == 0 ? 12 : _now.hour % 12; + final amPm = _now.hour < 12 ? 'AM' : 'PM'; + timeString = '$hour:${_now.minute.toString().padLeft(2, '0')} $amPm'; + } else { + timeString = '${_now.hour.toString().padLeft(2, '0')}:${_now.minute.toString().padLeft(2, '0')}'; + } return Align( alignment: _alignment, diff --git a/test/infrastructure/repositories/hybrid_photo_repository_test.dart b/test/infrastructure/repositories/hybrid_photo_repository_test.dart index 0b8533d..078814a 100644 --- a/test/infrastructure/repositories/hybrid_photo_repository_test.dart +++ b/test/infrastructure/repositories/hybrid_photo_repository_test.dart @@ -15,6 +15,9 @@ class FakeConfigProvider extends ChangeNotifier implements ConfigProvider { @override Future save() async {} + @override + bool useAmPm = false; + @override String get activeSourceType => ''; diff --git a/test/infrastructure/services/app_initializer_test.dart b/test/infrastructure/services/app_initializer_test.dart index cb4c23d..8874bfa 100644 --- a/test/infrastructure/services/app_initializer_test.dart +++ b/test/infrastructure/services/app_initializer_test.dart @@ -21,6 +21,9 @@ class FakeConfigProvider extends ChangeNotifier implements ConfigProvider { @override Future save() async {} + @override + bool useAmPm = false; + @override String get activeSourceType => ''; diff --git a/test/infrastructure/services/photo_service_directory_change_test.dart b/test/infrastructure/services/photo_service_directory_change_test.dart index b321290..2fcad7e 100644 --- a/test/infrastructure/services/photo_service_directory_change_test.dart +++ b/test/infrastructure/services/photo_service_directory_change_test.dart @@ -53,6 +53,9 @@ class MockConfigProvider extends ChangeNotifier implements ConfigProvider { @override String? get customPhotoPath => _customPhotoPath; + @override + bool useAmPm = false; + @override set customPhotoPath(String? value) { _customPhotoPath = value;