diff --git a/lib/I18n/I18nKeys.h b/lib/I18n/I18nKeys.h index f089d2b9..de304830 100644 --- a/lib/I18n/I18nKeys.h +++ b/lib/I18n/I18nKeys.h @@ -119,6 +119,7 @@ enum class StrId : uint16_t { STR_CAT_READER, STR_CAT_CONTROLS, STR_CAT_SYSTEM, + STR_CAT_CLOCK, STR_SLEEP_SCREEN, STR_SLEEP_COVER_MODE, STR_STATUS_BAR, @@ -371,10 +372,24 @@ enum class StrId : uint16_t { STR_PREFERRED_PORTRAIT, STR_PREFERRED_LANDSCAPE, STR_CHOOSE_SOMETHING, - STR_HOME_SCREEN_CLOCK, + STR_CLOCK, STR_CLOCK_AMPM, STR_CLOCK_24H, STR_SET_TIME, + STR_CLOCK_SIZE, + STR_CLOCK_SIZE_SMALL, + STR_CLOCK_SIZE_MEDIUM, + STR_CLOCK_SIZE_LARGE, + STR_TIMEZONE, + STR_TZ_UTC, + STR_TZ_EASTERN, + STR_TZ_CENTRAL, + STR_TZ_MOUNTAIN, + STR_TZ_PACIFIC, + STR_TZ_ALASKA, + STR_TZ_HAWAII, + STR_TZ_CUSTOM, + STR_SET_UTC_OFFSET, // Sentinel - must be last _COUNT }; diff --git a/lib/I18n/translations/czech.yaml b/lib/I18n/translations/czech.yaml index 8072502b..394d6a47 100644 --- a/lib/I18n/translations/czech.yaml +++ b/lib/I18n/translations/czech.yaml @@ -85,6 +85,7 @@ STR_CAT_DISPLAY: "Displej" STR_CAT_READER: "Čtečka" STR_CAT_CONTROLS: "Ovládací prvky" STR_CAT_SYSTEM: "Systém" +STR_CAT_CLOCK: "Hodiny" STR_SLEEP_SCREEN: "Obrazovka spánku" STR_SLEEP_COVER_MODE: "Obrazovka spánku Režim krytu" STR_STATUS_BAR: "Stavový řádek" @@ -316,7 +317,21 @@ STR_BOOK_S_STYLE: "Styl knihy" STR_EMBEDDED_STYLE: "Vložený styl" STR_OPDS_SERVER_URL: "URL serveru OPDS" STR_CHOOSE_SOMETHING: "Vyberte si něco ke čtení" -STR_HOME_SCREEN_CLOCK: "Hodiny na domovské obrazovce" +STR_CLOCK: "Hodiny" STR_CLOCK_AMPM: "AM/PM" STR_CLOCK_24H: "24 hodin" STR_SET_TIME: "Nastavit čas" +STR_CLOCK_SIZE: "Clock Size" +STR_CLOCK_SIZE_SMALL: "Small" +STR_CLOCK_SIZE_MEDIUM: "Medium" +STR_CLOCK_SIZE_LARGE: "Large" +STR_TIMEZONE: "Timezone" +STR_TZ_UTC: "UTC" +STR_TZ_EASTERN: "Eastern" +STR_TZ_CENTRAL: "Central" +STR_TZ_MOUNTAIN: "Mountain" +STR_TZ_PACIFIC: "Pacific" +STR_TZ_ALASKA: "Alaska" +STR_TZ_HAWAII: "Hawaii" +STR_TZ_CUSTOM: "Custom" +STR_SET_UTC_OFFSET: "Set UTC Offset" diff --git a/lib/I18n/translations/english.yaml b/lib/I18n/translations/english.yaml index 936bd171..7ebbcfdd 100644 --- a/lib/I18n/translations/english.yaml +++ b/lib/I18n/translations/english.yaml @@ -85,6 +85,7 @@ STR_CAT_DISPLAY: "Display" STR_CAT_READER: "Reader" STR_CAT_CONTROLS: "Controls" STR_CAT_SYSTEM: "System" +STR_CAT_CLOCK: "Clock" STR_SLEEP_SCREEN: "Sleep Screen" STR_SLEEP_COVER_MODE: "Sleep Screen Cover Mode" STR_STATUS_BAR: "Status Bar" @@ -337,7 +338,21 @@ STR_OVERRIDE_LETTERBOX_FILL: "Override Letterbox Fill" STR_PREFERRED_PORTRAIT: "Preferred Portrait" STR_PREFERRED_LANDSCAPE: "Preferred Landscape" STR_CHOOSE_SOMETHING: "Choose something to read" -STR_HOME_SCREEN_CLOCK: "Home Screen Clock" +STR_CLOCK: "Clock" STR_CLOCK_AMPM: "AM/PM" STR_CLOCK_24H: "24 Hour" STR_SET_TIME: "Set Time" +STR_CLOCK_SIZE: "Clock Size" +STR_CLOCK_SIZE_SMALL: "Small" +STR_CLOCK_SIZE_MEDIUM: "Medium" +STR_CLOCK_SIZE_LARGE: "Large" +STR_TIMEZONE: "Timezone" +STR_TZ_UTC: "UTC" +STR_TZ_EASTERN: "Eastern" +STR_TZ_CENTRAL: "Central" +STR_TZ_MOUNTAIN: "Mountain" +STR_TZ_PACIFIC: "Pacific" +STR_TZ_ALASKA: "Alaska" +STR_TZ_HAWAII: "Hawaii" +STR_TZ_CUSTOM: "Custom" +STR_SET_UTC_OFFSET: "Set UTC Offset" diff --git a/lib/I18n/translations/french.yaml b/lib/I18n/translations/french.yaml index 91db26f8..8b2eebec 100644 --- a/lib/I18n/translations/french.yaml +++ b/lib/I18n/translations/french.yaml @@ -85,6 +85,7 @@ STR_CAT_DISPLAY: "Affichage" STR_CAT_READER: "Lecteur" STR_CAT_CONTROLS: "Commandes" STR_CAT_SYSTEM: "Système" +STR_CAT_CLOCK: "Horloge" STR_SLEEP_SCREEN: "Écran de veille" STR_SLEEP_COVER_MODE: "Mode d’image de l’écran de veille" STR_STATUS_BAR: "Barre d’état" @@ -316,7 +317,21 @@ STR_BOOK_S_STYLE: "Style du livre" STR_EMBEDDED_STYLE: "Style intégré" STR_OPDS_SERVER_URL: "URL du serveur OPDS" STR_CHOOSE_SOMETHING: "Choisissez quelque chose à lire" -STR_HOME_SCREEN_CLOCK: "Horloge écran d'accueil" +STR_CLOCK: "Horloge" STR_CLOCK_AMPM: "AM/PM" STR_CLOCK_24H: "24 heures" STR_SET_TIME: "Régler l'heure" +STR_CLOCK_SIZE: "Clock Size" +STR_CLOCK_SIZE_SMALL: "Small" +STR_CLOCK_SIZE_MEDIUM: "Medium" +STR_CLOCK_SIZE_LARGE: "Large" +STR_TIMEZONE: "Timezone" +STR_TZ_UTC: "UTC" +STR_TZ_EASTERN: "Eastern" +STR_TZ_CENTRAL: "Central" +STR_TZ_MOUNTAIN: "Mountain" +STR_TZ_PACIFIC: "Pacific" +STR_TZ_ALASKA: "Alaska" +STR_TZ_HAWAII: "Hawaii" +STR_TZ_CUSTOM: "Custom" +STR_SET_UTC_OFFSET: "Set UTC Offset" diff --git a/lib/I18n/translations/german.yaml b/lib/I18n/translations/german.yaml index 7b5099a9..e4ac32f4 100644 --- a/lib/I18n/translations/german.yaml +++ b/lib/I18n/translations/german.yaml @@ -85,6 +85,7 @@ STR_CAT_DISPLAY: "Anzeige" STR_CAT_READER: "Lesen" STR_CAT_CONTROLS: "Bedienung" STR_CAT_SYSTEM: "System" +STR_CAT_CLOCK: "Uhr" STR_SLEEP_SCREEN: "Standby-Bild" STR_SLEEP_COVER_MODE: "Standby-Bildmodus" STR_STATUS_BAR: "Statusleiste" @@ -316,7 +317,21 @@ STR_BOOK_S_STYLE: "Buch-Stil" STR_EMBEDDED_STYLE: "Eingebetteter Stil" STR_OPDS_SERVER_URL: "OPDS-Server-URL" STR_CHOOSE_SOMETHING: "Wähle etwas zum Lesen" -STR_HOME_SCREEN_CLOCK: "Startbildschirm-Uhr" +STR_CLOCK: "Uhr" STR_CLOCK_AMPM: "AM/PM" STR_CLOCK_24H: "24 Stunden" STR_SET_TIME: "Uhrzeit einstellen" +STR_CLOCK_SIZE: "Clock Size" +STR_CLOCK_SIZE_SMALL: "Small" +STR_CLOCK_SIZE_MEDIUM: "Medium" +STR_CLOCK_SIZE_LARGE: "Large" +STR_TIMEZONE: "Timezone" +STR_TZ_UTC: "UTC" +STR_TZ_EASTERN: "Eastern" +STR_TZ_CENTRAL: "Central" +STR_TZ_MOUNTAIN: "Mountain" +STR_TZ_PACIFIC: "Pacific" +STR_TZ_ALASKA: "Alaska" +STR_TZ_HAWAII: "Hawaii" +STR_TZ_CUSTOM: "Custom" +STR_SET_UTC_OFFSET: "Set UTC Offset" diff --git a/lib/I18n/translations/portuguese.yaml b/lib/I18n/translations/portuguese.yaml index b980c779..0f5d5690 100644 --- a/lib/I18n/translations/portuguese.yaml +++ b/lib/I18n/translations/portuguese.yaml @@ -85,6 +85,7 @@ STR_CAT_DISPLAY: "Tela" STR_CAT_READER: "Leitor" STR_CAT_CONTROLS: "Controles" STR_CAT_SYSTEM: "Sistema" +STR_CAT_CLOCK: "Relógio" STR_SLEEP_SCREEN: "Tela de repouso" STR_SLEEP_COVER_MODE: "Modo capa tela repouso" STR_STATUS_BAR: "Barra de status" @@ -316,7 +317,21 @@ STR_BOOK_S_STYLE: "Estilo do livro" STR_EMBEDDED_STYLE: "Estilo embutido" STR_OPDS_SERVER_URL: "URL do servidor OPDS" STR_CHOOSE_SOMETHING: "Escolha algo para ler" -STR_HOME_SCREEN_CLOCK: "Relógio da tela inicial" +STR_CLOCK: "Relógio" STR_CLOCK_AMPM: "AM/PM" STR_CLOCK_24H: "24 horas" STR_SET_TIME: "Definir hora" +STR_CLOCK_SIZE: "Clock Size" +STR_CLOCK_SIZE_SMALL: "Small" +STR_CLOCK_SIZE_MEDIUM: "Medium" +STR_CLOCK_SIZE_LARGE: "Large" +STR_TIMEZONE: "Timezone" +STR_TZ_UTC: "UTC" +STR_TZ_EASTERN: "Eastern" +STR_TZ_CENTRAL: "Central" +STR_TZ_MOUNTAIN: "Mountain" +STR_TZ_PACIFIC: "Pacific" +STR_TZ_ALASKA: "Alaska" +STR_TZ_HAWAII: "Hawaii" +STR_TZ_CUSTOM: "Custom" +STR_SET_UTC_OFFSET: "Set UTC Offset" diff --git a/lib/I18n/translations/russian.yaml b/lib/I18n/translations/russian.yaml index fdfee21b..30811196 100644 --- a/lib/I18n/translations/russian.yaml +++ b/lib/I18n/translations/russian.yaml @@ -85,6 +85,7 @@ STR_CAT_DISPLAY: "Экран" STR_CAT_READER: "Чтение" STR_CAT_CONTROLS: "Управление" STR_CAT_SYSTEM: "Система" +STR_CAT_CLOCK: "Часы" STR_SLEEP_SCREEN: "Экран сна" STR_SLEEP_COVER_MODE: "Режим обложки сна" STR_STATUS_BAR: "Строка состояния" @@ -316,7 +317,21 @@ STR_BOOK_S_STYLE: "Стиль книги" STR_EMBEDDED_STYLE: "Встроенный стиль" STR_OPDS_SERVER_URL: "URL OPDS сервера" STR_CHOOSE_SOMETHING: "Выберите что-нибудь для чтения" -STR_HOME_SCREEN_CLOCK: "Часы на главном экране" +STR_CLOCK: "Часы" STR_CLOCK_AMPM: "AM/PM" STR_CLOCK_24H: "24 часа" STR_SET_TIME: "Установить время" +STR_CLOCK_SIZE: "Clock Size" +STR_CLOCK_SIZE_SMALL: "Small" +STR_CLOCK_SIZE_MEDIUM: "Medium" +STR_CLOCK_SIZE_LARGE: "Large" +STR_TIMEZONE: "Timezone" +STR_TZ_UTC: "UTC" +STR_TZ_EASTERN: "Eastern" +STR_TZ_CENTRAL: "Central" +STR_TZ_MOUNTAIN: "Mountain" +STR_TZ_PACIFIC: "Pacific" +STR_TZ_ALASKA: "Alaska" +STR_TZ_HAWAII: "Hawaii" +STR_TZ_CUSTOM: "Custom" +STR_SET_UTC_OFFSET: "Set UTC Offset" diff --git a/lib/I18n/translations/spanish.yaml b/lib/I18n/translations/spanish.yaml index 1a3db532..2cd81e42 100644 --- a/lib/I18n/translations/spanish.yaml +++ b/lib/I18n/translations/spanish.yaml @@ -85,6 +85,7 @@ STR_CAT_DISPLAY: "Pantalla" STR_CAT_READER: "Lector" STR_CAT_CONTROLS: "Control" STR_CAT_SYSTEM: "Sistema" +STR_CAT_CLOCK: "Reloj" STR_SLEEP_SCREEN: "Salva Pantallas" STR_SLEEP_COVER_MODE: "Modo de salva pantallas" STR_STATUS_BAR: "Barra de estado" @@ -316,7 +317,21 @@ STR_BOOK_S_STYLE: "Estilo del libro" STR_EMBEDDED_STYLE: "Estilo integrado" STR_OPDS_SERVER_URL: "URL del servidor OPDS" STR_CHOOSE_SOMETHING: "Elige algo para leer" -STR_HOME_SCREEN_CLOCK: "Reloj de pantalla de inicio" +STR_CLOCK: "Reloj" STR_CLOCK_AMPM: "AM/PM" STR_CLOCK_24H: "24 horas" STR_SET_TIME: "Establecer hora" +STR_CLOCK_SIZE: "Clock Size" +STR_CLOCK_SIZE_SMALL: "Small" +STR_CLOCK_SIZE_MEDIUM: "Medium" +STR_CLOCK_SIZE_LARGE: "Large" +STR_TIMEZONE: "Timezone" +STR_TZ_UTC: "UTC" +STR_TZ_EASTERN: "Eastern" +STR_TZ_CENTRAL: "Central" +STR_TZ_MOUNTAIN: "Mountain" +STR_TZ_PACIFIC: "Pacific" +STR_TZ_ALASKA: "Alaska" +STR_TZ_HAWAII: "Hawaii" +STR_TZ_CUSTOM: "Custom" +STR_SET_UTC_OFFSET: "Set UTC Offset" diff --git a/lib/I18n/translations/swedish.yaml b/lib/I18n/translations/swedish.yaml index 57341a78..b9aae499 100644 --- a/lib/I18n/translations/swedish.yaml +++ b/lib/I18n/translations/swedish.yaml @@ -85,6 +85,7 @@ STR_CAT_DISPLAY: "Skärm" STR_CAT_READER: "Läsare" STR_CAT_CONTROLS: "Kontroller" STR_CAT_SYSTEM: "System" +STR_CAT_CLOCK: "Klocka" STR_SLEEP_SCREEN: "Viloskärm" STR_SLEEP_COVER_MODE: "Viloskärmens omslagsläge" STR_STATUS_BAR: "Statusrad" @@ -316,7 +317,21 @@ STR_BOOK_S_STYLE: "Bokstil" STR_EMBEDDED_STYLE: "Inbäddad stil" STR_OPDS_SERVER_URL: "OPDS-serveradress" STR_CHOOSE_SOMETHING: "Välj något att läsa" -STR_HOME_SCREEN_CLOCK: "Klocka på hemskärmen" +STR_CLOCK: "Klocka" STR_CLOCK_AMPM: "AM/PM" STR_CLOCK_24H: "24 timmar" STR_SET_TIME: "Ställ in tid" +STR_CLOCK_SIZE: "Clock Size" +STR_CLOCK_SIZE_SMALL: "Small" +STR_CLOCK_SIZE_MEDIUM: "Medium" +STR_CLOCK_SIZE_LARGE: "Large" +STR_TIMEZONE: "Timezone" +STR_TZ_UTC: "UTC" +STR_TZ_EASTERN: "Eastern" +STR_TZ_CENTRAL: "Central" +STR_TZ_MOUNTAIN: "Mountain" +STR_TZ_PACIFIC: "Pacific" +STR_TZ_ALASKA: "Alaska" +STR_TZ_HAWAII: "Hawaii" +STR_TZ_CUSTOM: "Custom" +STR_SET_UTC_OFFSET: "Set UTC Offset" diff --git a/src/CrossPointSettings.cpp b/src/CrossPointSettings.cpp index a1e60612..b5c0fa40 100644 --- a/src/CrossPointSettings.cpp +++ b/src/CrossPointSettings.cpp @@ -4,6 +4,7 @@ #include #include +#include #include #include @@ -138,7 +139,10 @@ uint8_t CrossPointSettings::writeSettings(FsFile& file, bool count_only) const { // New fields added at end for backward compatibility writer.writeItem(file, preferredPortrait); writer.writeItem(file, preferredLandscape); - writer.writeItem(file, homeScreenClock); + writer.writeItem(file, clockFormat); + writer.writeItem(file, clockSize); + writer.writeItem(file, timezone); + writer.writeItem(file, timezoneOffsetHours); return writer.item_count; } @@ -267,14 +271,19 @@ bool CrossPointSettings::loadFromFile() { if (++settingsRead >= fileSettingsCount) break; readAndValidate(inputFile, sleepScreenLetterboxFill, SLEEP_SCREEN_LETTERBOX_FILL_COUNT); if (++settingsRead >= fileSettingsCount) break; - { uint8_t _ignore; serialization::readPod(inputFile, _ignore); } // legacy: sleepScreenGradientDir - if (++settingsRead >= fileSettingsCount) break; // New fields added at end for backward compatibility readAndValidate(inputFile, preferredPortrait, ORIENTATION_COUNT); if (++settingsRead >= fileSettingsCount) break; readAndValidate(inputFile, preferredLandscape, ORIENTATION_COUNT); if (++settingsRead >= fileSettingsCount) break; - readAndValidate(inputFile, homeScreenClock, CLOCK_FORMAT_COUNT); + readAndValidate(inputFile, clockFormat, CLOCK_FORMAT_COUNT); + if (++settingsRead >= fileSettingsCount) break; + readAndValidate(inputFile, clockSize, CLOCK_SIZE_COUNT); + if (++settingsRead >= fileSettingsCount) break; + readAndValidate(inputFile, timezone, TZ_COUNT); + if (++settingsRead >= fileSettingsCount) break; + serialization::readPod(inputFile, timezoneOffsetHours); + if (timezoneOffsetHours < -12 || timezoneOffsetHours > 14) timezoneOffsetHours = 0; if (++settingsRead >= fileSettingsCount) break; } while (false); @@ -442,3 +451,31 @@ int CrossPointSettings::getReaderFontId() const { #endif } } + +const char* CrossPointSettings::getTimezonePosixStr() const { + switch (timezone) { + case TZ_EASTERN: + return "EST5EDT,M3.2.0,M11.1.0"; + case TZ_CENTRAL: + return "CST6CDT,M3.2.0,M11.1.0"; + case TZ_MOUNTAIN: + return "MST7MDT,M3.2.0,M11.1.0"; + case TZ_PACIFIC: + return "PST8PDT,M3.2.0,M11.1.0"; + case TZ_ALASKA: + return "AKST9AKDT,M3.2.0,M11.1.0"; + case TZ_HAWAII: + return "HST10"; + case TZ_CUSTOM: { + // Build "UTC" string where offset sign is inverted per POSIX convention + // POSIX TZ: positive = west of UTC, so we negate the user-facing offset + static char buf[16]; + int posixOffset = -timezoneOffsetHours; + snprintf(buf, sizeof(buf), "UTC%d", posixOffset); + return buf; + } + case TZ_UTC: + default: + return "UTC0"; + } +} diff --git a/src/CrossPointSettings.h b/src/CrossPointSettings.h index 732449c9..2d4b10e5 100644 --- a/src/CrossPointSettings.h +++ b/src/CrossPointSettings.h @@ -131,6 +131,22 @@ class CrossPointSettings { // Home screen clock format enum CLOCK_FORMAT { CLOCK_OFF = 0, CLOCK_AMPM = 1, CLOCK_24H = 2, CLOCK_FORMAT_COUNT }; + // Clock size + enum CLOCK_SIZE { CLOCK_SIZE_SMALL = 0, CLOCK_SIZE_MEDIUM = 1, CLOCK_SIZE_LARGE = 2, CLOCK_SIZE_COUNT }; + + // Timezone presets + enum TIMEZONE { + TZ_UTC = 0, + TZ_EASTERN = 1, + TZ_CENTRAL = 2, + TZ_MOUNTAIN = 3, + TZ_PACIFIC = 4, + TZ_ALASKA = 5, + TZ_HAWAII = 6, + TZ_CUSTOM = 7, + TZ_COUNT + }; + // Sleep screen settings uint8_t sleepScreen = DARK; // Sleep screen cover mode settings @@ -192,8 +208,15 @@ class CrossPointSettings { uint8_t preferredPortrait = PORTRAIT; uint8_t preferredLandscape = LANDSCAPE_CW; - // Home screen clock display format (OFF by default) - uint8_t homeScreenClock = CLOCK_OFF; + // Clock display format (OFF by default) + uint8_t clockFormat = CLOCK_OFF; + // Clock display size + uint8_t clockSize = CLOCK_SIZE_SMALL; + + // Timezone setting + uint8_t timezone = TZ_UTC; + // Custom timezone offset in hours from UTC (-12 to +14) + int8_t timezoneOffsetHours = 0; ~CrossPointSettings() = default; @@ -214,6 +237,7 @@ class CrossPointSettings { float getReaderLineCompression() const; unsigned long getSleepTimeoutMs() const; int getRefreshFrequency() const; + const char* getTimezonePosixStr() const; }; // Helper macro to access settings diff --git a/src/SettingsList.h b/src/SettingsList.h index 91c6a3e7..a5d1527b 100644 --- a/src/SettingsList.h +++ b/src/SettingsList.h @@ -76,9 +76,17 @@ inline std::vector getSettingsList() { {StrId::STR_THEME_CLASSIC, StrId::STR_THEME_LYRA}, "uiTheme", StrId::STR_CAT_DISPLAY), SettingInfo::Toggle(StrId::STR_SUNLIGHT_FADING_FIX, &CrossPointSettings::fadingFix, "fadingFix", StrId::STR_CAT_DISPLAY), - SettingInfo::Enum(StrId::STR_HOME_SCREEN_CLOCK, &CrossPointSettings::homeScreenClock, - {StrId::STR_STATE_OFF, StrId::STR_CLOCK_AMPM, StrId::STR_CLOCK_24H}, "homeScreenClock", - StrId::STR_CAT_DISPLAY), + // --- Clock --- + SettingInfo::Enum(StrId::STR_CLOCK, &CrossPointSettings::clockFormat, + {StrId::STR_STATE_OFF, StrId::STR_CLOCK_AMPM, StrId::STR_CLOCK_24H}, "clockFormat", + StrId::STR_CAT_CLOCK), + SettingInfo::Enum(StrId::STR_CLOCK_SIZE, &CrossPointSettings::clockSize, + {StrId::STR_CLOCK_SIZE_SMALL, StrId::STR_CLOCK_SIZE_MEDIUM, StrId::STR_CLOCK_SIZE_LARGE}, + "clockSize", StrId::STR_CAT_CLOCK), + SettingInfo::Enum(StrId::STR_TIMEZONE, &CrossPointSettings::timezone, + {StrId::STR_TZ_UTC, StrId::STR_TZ_EASTERN, StrId::STR_TZ_CENTRAL, StrId::STR_TZ_MOUNTAIN, + StrId::STR_TZ_PACIFIC, StrId::STR_TZ_ALASKA, StrId::STR_TZ_HAWAII, StrId::STR_TZ_CUSTOM}, + "timezone", StrId::STR_CAT_CLOCK), // --- Reader --- SettingInfo::DynamicEnum( diff --git a/src/activities/home/HomeActivity.cpp b/src/activities/home/HomeActivity.cpp index 581166f2..90d1abb4 100644 --- a/src/activities/home/HomeActivity.cpp +++ b/src/activities/home/HomeActivity.cpp @@ -11,7 +11,6 @@ #include #include -#include #include #include "Battery.h" @@ -192,19 +191,6 @@ void HomeActivity::freeCoverBuffer() { } void HomeActivity::loop() { - // Refresh the screen when the displayed minute changes (clock update) - if (SETTINGS.homeScreenClock != CrossPointSettings::CLOCK_OFF) { - time_t now = time(nullptr); - struct tm* t = localtime(&now); - if (t != nullptr && t->tm_year > 100) { - const int currentMinute = t->tm_hour * 60 + t->tm_min; - if (lastRenderedMinute >= 0 && currentMinute != lastRenderedMinute) { - requestUpdate(); - } - lastRenderedMinute = currentMinute; - } - } - const int menuCount = getMenuItemCount(); buttonNavigator.onNext([this, menuCount] { diff --git a/src/activities/home/HomeActivity.h b/src/activities/home/HomeActivity.h index 57e111cb..5359bf79 100644 --- a/src/activities/home/HomeActivity.h +++ b/src/activities/home/HomeActivity.h @@ -16,7 +16,6 @@ class HomeActivity final : public Activity { bool recentsLoaded = false; bool firstRenderDone = false; bool hasOpdsUrl = false; - int lastRenderedMinute = -1; // Track displayed minute for clock auto-update bool coverRendered = false; // Track if cover has been rendered once bool coverBufferStored = false; // Track if cover buffer is stored uint8_t* coverBuffer = nullptr; // HomeActivity's own buffer for cover image diff --git a/src/activities/settings/SetTimezoneOffsetActivity.cpp b/src/activities/settings/SetTimezoneOffsetActivity.cpp new file mode 100644 index 00000000..a03ecd3c --- /dev/null +++ b/src/activities/settings/SetTimezoneOffsetActivity.cpp @@ -0,0 +1,101 @@ +#include "SetTimezoneOffsetActivity.h" + +#include +#include + +#include +#include + +#include "CrossPointSettings.h" +#include "MappedInputManager.h" +#include "components/UITheme.h" +#include "fontIds.h" + +void SetTimezoneOffsetActivity::onEnter() { + Activity::onEnter(); + offsetHours = SETTINGS.timezoneOffsetHours; + requestUpdate(); +} + +void SetTimezoneOffsetActivity::onExit() { Activity::onExit(); } + +void SetTimezoneOffsetActivity::loop() { + if (mappedInput.wasPressed(MappedInputManager::Button::Back)) { + onBack(); + return; + } + + if (mappedInput.wasPressed(MappedInputManager::Button::Confirm)) { + SETTINGS.timezoneOffsetHours = offsetHours; + SETTINGS.saveToFile(); + // Apply timezone immediately + setenv("TZ", SETTINGS.getTimezonePosixStr(), 1); + tzset(); + onBack(); + return; + } + + if (mappedInput.wasPressed(MappedInputManager::Button::Up)) { + if (offsetHours < 14) { + offsetHours++; + requestUpdate(); + } + return; + } + + if (mappedInput.wasPressed(MappedInputManager::Button::Down)) { + if (offsetHours > -12) { + offsetHours--; + requestUpdate(); + } + return; + } +} + +void SetTimezoneOffsetActivity::render(Activity::RenderLock&&) { + renderer.clearScreen(); + + const auto pageWidth = renderer.getScreenWidth(); + const int lineHeight12 = renderer.getLineHeight(UI_12_FONT_ID); + + // Title + renderer.drawCenteredText(UI_12_FONT_ID, 20, tr(STR_SET_UTC_OFFSET), true, EpdFontFamily::BOLD); + + // Format the offset string + char offsetStr[16]; + if (offsetHours >= 0) { + snprintf(offsetStr, sizeof(offsetStr), "UTC+%d", offsetHours); + } else { + snprintf(offsetStr, sizeof(offsetStr), "UTC%d", offsetHours); + } + + const int textWidth = renderer.getTextWidth(UI_12_FONT_ID, offsetStr); + const int startX = (pageWidth - textWidth) / 2; + const int valueY = 80; + + // Draw selection highlight + constexpr int highlightPad = 10; + renderer.fillRoundedRect(startX - highlightPad, valueY - 4, textWidth + highlightPad * 2, lineHeight12 + 8, 6, + Color::LightGray); + + // Draw the offset text + renderer.drawText(UI_12_FONT_ID, startX, valueY, offsetStr, true); + + // Draw up/down arrows + const int arrowX = pageWidth / 2; + const int arrowUpY = valueY - 20; + const int arrowDownY = valueY + lineHeight12 + 12; + constexpr int arrowSize = 6; + for (int row = 0; row < arrowSize; row++) { + renderer.drawLine(arrowX - row, arrowUpY + row, arrowX + row, arrowUpY + row); + } + for (int row = 0; row < arrowSize; row++) { + renderer.drawLine(arrowX - row, arrowDownY + arrowSize - 1 - row, arrowX + row, arrowDownY + arrowSize - 1 - row); + } + + // Button hints + const auto labels = mappedInput.mapLabels(tr(STR_BACK), tr(STR_SAVE), tr(STR_DIR_UP), tr(STR_DIR_DOWN)); + GUI.drawButtonHints(renderer, labels.btn1, labels.btn2, labels.btn3, labels.btn4); + + renderer.displayBuffer(); +} diff --git a/src/activities/settings/SetTimezoneOffsetActivity.h b/src/activities/settings/SetTimezoneOffsetActivity.h new file mode 100644 index 00000000..7eeadd90 --- /dev/null +++ b/src/activities/settings/SetTimezoneOffsetActivity.h @@ -0,0 +1,21 @@ +#pragma once + +#include + +#include "activities/Activity.h" + +class SetTimezoneOffsetActivity final : public Activity { + public: + explicit SetTimezoneOffsetActivity(GfxRenderer& renderer, MappedInputManager& mappedInput, + const std::function& onBack) + : Activity("SetTZOffset", renderer, mappedInput), onBack(onBack) {} + + void onEnter() override; + void onExit() override; + void loop() override; + void render(Activity::RenderLock&&) override; + + private: + const std::function onBack; + int8_t offsetHours = 0; +}; diff --git a/src/activities/settings/SettingsActivity.cpp b/src/activities/settings/SettingsActivity.cpp index 3adda2e7..6ff173e8 100644 --- a/src/activities/settings/SettingsActivity.cpp +++ b/src/activities/settings/SettingsActivity.cpp @@ -3,6 +3,9 @@ #include #include +#include +#include + #include "ButtonRemapActivity.h" #include "CalibreSettingsActivity.h" #include "ClearCacheActivity.h" @@ -12,19 +15,22 @@ #include "MappedInputManager.h" #include "OtaUpdateActivity.h" #include "SetTimeActivity.h" +#include "SetTimezoneOffsetActivity.h" #include "SettingsList.h" #include "activities/network/WifiSelectionActivity.h" #include "components/UITheme.h" #include "fontIds.h" const StrId SettingsActivity::categoryNames[categoryCount] = {StrId::STR_CAT_DISPLAY, StrId::STR_CAT_READER, - StrId::STR_CAT_CONTROLS, StrId::STR_CAT_SYSTEM}; + StrId::STR_CAT_CONTROLS, StrId::STR_CAT_SYSTEM, + StrId::STR_CAT_CLOCK}; void SettingsActivity::onEnter() { Activity::onEnter(); // Build per-category vectors from the shared settings list displaySettings.clear(); + clockSettings.clear(); readerSettings.clear(); controlsSettings.clear(); systemSettings.clear(); @@ -33,6 +39,8 @@ void SettingsActivity::onEnter() { if (setting.category == StrId::STR_NONE_OPT) continue; if (setting.category == StrId::STR_CAT_DISPLAY) { displaySettings.push_back(std::move(setting)); + } else if (setting.category == StrId::STR_CAT_CLOCK) { + clockSettings.push_back(std::move(setting)); } else if (setting.category == StrId::STR_CAT_READER) { readerSettings.push_back(std::move(setting)); } else if (setting.category == StrId::STR_CAT_CONTROLS) { @@ -44,7 +52,7 @@ void SettingsActivity::onEnter() { } // Append device-only ACTION items - displaySettings.push_back(SettingInfo::Action(StrId::STR_SET_TIME, SettingAction::SetTime)); + rebuildClockActions(); controlsSettings.insert(controlsSettings.begin(), SettingInfo::Action(StrId::STR_REMAP_FRONT_BUTTONS, SettingAction::RemapFrontButtons)); systemSettings.push_back(SettingInfo::Action(StrId::STR_WIFI_NETWORKS, SettingAction::Network)); @@ -136,6 +144,9 @@ void SettingsActivity::loop() { case 3: currentSettings = &systemSettings; break; + case 4: + currentSettings = &clockSettings; + break; } settingsCount = static_cast(currentSettings->size()); } @@ -207,6 +218,9 @@ void SettingsActivity::toggleCurrentSetting() { case SettingAction::SetTime: enterSubActivity(new SetTimeActivity(renderer, mappedInput, onComplete)); break; + case SettingAction::SetTimezoneOffset: + enterSubActivity(new SetTimezoneOffsetActivity(renderer, mappedInput, onComplete)); + break; case SettingAction::None: // Do nothing break; @@ -216,6 +230,37 @@ void SettingsActivity::toggleCurrentSetting() { } SETTINGS.saveToFile(); + + // Apply timezone whenever settings change (idempotent, cheap) + setenv("TZ", SETTINGS.getTimezonePosixStr(), 1); + tzset(); + + // Rebuild clock actions (show/hide "Set UTC Offset" based on timezone selection) + rebuildClockActions(); +} + +void SettingsActivity::rebuildClockActions() { + // Remove any existing ACTION items from clockSettings (keep enum settings from getSettingsList) + clockSettings.erase(std::remove_if(clockSettings.begin(), clockSettings.end(), + [](const SettingInfo& s) { return s.type == SettingType::ACTION; }), + clockSettings.end()); + + // Always add Set Time + clockSettings.push_back(SettingInfo::Action(StrId::STR_SET_TIME, SettingAction::SetTime)); + + // Only add Set UTC Offset when timezone is set to Custom + if (SETTINGS.timezone == CrossPointSettings::TZ_CUSTOM) { + clockSettings.push_back(SettingInfo::Action(StrId::STR_SET_UTC_OFFSET, SettingAction::SetTimezoneOffset)); + } + + // Update settingsCount if we're currently viewing the clock category + if (currentSettings == &clockSettings) { + settingsCount = static_cast(clockSettings.size()); + // Clamp selection to avoid pointing past the end of the list + if (selectedSettingIndex > settingsCount) { + selectedSettingIndex = settingsCount; + } + } } void SettingsActivity::render(Activity::RenderLock&&) { diff --git a/src/activities/settings/SettingsActivity.h b/src/activities/settings/SettingsActivity.h index 10c7e1a0..8f375ab6 100644 --- a/src/activities/settings/SettingsActivity.h +++ b/src/activities/settings/SettingsActivity.h @@ -22,6 +22,7 @@ enum class SettingAction { CheckForUpdates, Language, SetTime, + SetTimezoneOffset, }; struct SettingInfo { @@ -143,6 +144,7 @@ class SettingsActivity final : public ActivityWithSubactivity { // Per-category settings derived from shared list + device-only actions std::vector displaySettings; + std::vector clockSettings; std::vector readerSettings; std::vector controlsSettings; std::vector systemSettings; @@ -150,11 +152,12 @@ class SettingsActivity final : public ActivityWithSubactivity { const std::function onGoHome; - static constexpr int categoryCount = 4; + static constexpr int categoryCount = 5; static const StrId categoryNames[categoryCount]; void enterCategory(int categoryIndex); void toggleCurrentSetting(); + void rebuildClockActions(); public: explicit SettingsActivity(GfxRenderer& renderer, MappedInputManager& mappedInput, diff --git a/src/components/themes/BaseTheme.cpp b/src/components/themes/BaseTheme.cpp index 88e918d4..54abb232 100644 --- a/src/components/themes/BaseTheme.cpp +++ b/src/components/themes/BaseTheme.cpp @@ -263,19 +263,22 @@ void BaseTheme::drawHeader(const GfxRenderer& renderer, Rect rect, const char* t showBatteryPercentage); // Draw clock on the left side (symmetric with battery on the right) - if (SETTINGS.homeScreenClock != CrossPointSettings::CLOCK_OFF) { + if (SETTINGS.clockFormat != CrossPointSettings::CLOCK_OFF) { time_t now = time(nullptr); struct tm* t = localtime(&now); if (t != nullptr && t->tm_year > 100) { char timeBuf[16]; - if (SETTINGS.homeScreenClock == CrossPointSettings::CLOCK_24H) { + if (SETTINGS.clockFormat == CrossPointSettings::CLOCK_24H) { snprintf(timeBuf, sizeof(timeBuf), "%02d:%02d", t->tm_hour, t->tm_min); } else { int hour12 = t->tm_hour % 12; if (hour12 == 0) hour12 = 12; snprintf(timeBuf, sizeof(timeBuf), "%d:%02d %s", hour12, t->tm_min, t->tm_hour >= 12 ? "PM" : "AM"); } - renderer.drawText(SMALL_FONT_ID, rect.x + 12, rect.y + 5, timeBuf, true); + int clockFont = SMALL_FONT_ID; + if (SETTINGS.clockSize == CrossPointSettings::CLOCK_SIZE_MEDIUM) clockFont = UI_10_FONT_ID; + else if (SETTINGS.clockSize == CrossPointSettings::CLOCK_SIZE_LARGE) clockFont = UI_12_FONT_ID; + renderer.drawText(clockFont, rect.x + 12, rect.y + 5, timeBuf, true); } } diff --git a/src/components/themes/lyra/LyraTheme.cpp b/src/components/themes/lyra/LyraTheme.cpp index 50d3c4f1..5ad7a41a 100644 --- a/src/components/themes/lyra/LyraTheme.cpp +++ b/src/components/themes/lyra/LyraTheme.cpp @@ -116,19 +116,22 @@ void LyraTheme::drawHeader(const GfxRenderer& renderer, Rect rect, const char* t showBatteryPercentage); // Draw clock on the left side (symmetric with battery on the right) - if (SETTINGS.homeScreenClock != CrossPointSettings::CLOCK_OFF) { + if (SETTINGS.clockFormat != CrossPointSettings::CLOCK_OFF) { time_t now = time(nullptr); struct tm* t = localtime(&now); if (t != nullptr && t->tm_year > 100) { char timeBuf[16]; - if (SETTINGS.homeScreenClock == CrossPointSettings::CLOCK_24H) { + if (SETTINGS.clockFormat == CrossPointSettings::CLOCK_24H) { snprintf(timeBuf, sizeof(timeBuf), "%02d:%02d", t->tm_hour, t->tm_min); } else { int hour12 = t->tm_hour % 12; if (hour12 == 0) hour12 = 12; snprintf(timeBuf, sizeof(timeBuf), "%d:%02d %s", hour12, t->tm_min, t->tm_hour >= 12 ? "PM" : "AM"); } - renderer.drawText(SMALL_FONT_ID, rect.x + 12, rect.y + 5, timeBuf, true); + int clockFont = SMALL_FONT_ID; + if (SETTINGS.clockSize == CrossPointSettings::CLOCK_SIZE_MEDIUM) clockFont = UI_10_FONT_ID; + else if (SETTINGS.clockSize == CrossPointSettings::CLOCK_SIZE_LARGE) clockFont = UI_12_FONT_ID; + renderer.drawText(clockFont, rect.x + 12, rect.y + 5, timeBuf, true); } } diff --git a/src/main.cpp b/src/main.cpp index 7fd81f18..f41caa16 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -10,6 +10,7 @@ #include #include +#include #include #include @@ -325,6 +326,11 @@ void setup() { } SETTINGS.loadFromFile(); + + // Apply saved timezone setting on boot + setenv("TZ", SETTINGS.getTimezonePosixStr(), 1); + tzset(); + I18N.loadSettings(); KOREADER_STORE.loadFromFile(); UITheme::getInstance().reload(); @@ -441,6 +447,20 @@ void loop() { return; } + // Refresh screen when the displayed minute changes (clock in header) + if (SETTINGS.clockFormat != CrossPointSettings::CLOCK_OFF && currentActivity) { + static int lastRenderedMinute = -1; + time_t now = time(nullptr); + struct tm* t = localtime(&now); + if (t != nullptr && t->tm_year > 100) { + const int currentMinute = t->tm_hour * 60 + t->tm_min; + if (lastRenderedMinute >= 0 && currentMinute != lastRenderedMinute) { + currentActivity->requestUpdate(); + } + lastRenderedMinute = currentMinute; + } + } + const unsigned long activityStartTime = millis(); if (currentActivity) { currentActivity->loop(); diff --git a/src/util/TimeSync.cpp b/src/util/TimeSync.cpp index b545a875..65eabef2 100644 --- a/src/util/TimeSync.cpp +++ b/src/util/TimeSync.cpp @@ -5,6 +5,10 @@ #include #include +#include + +#include "CrossPointSettings.h" + namespace TimeSync { void startNtpSync() { @@ -12,6 +16,10 @@ void startNtpSync() { esp_sntp_stop(); } + // Apply timezone so NTP-synced time is displayed correctly + setenv("TZ", SETTINGS.getTimezonePosixStr(), 1); + tzset(); + esp_sntp_setoperatingmode(ESP_SNTP_OPMODE_POLL); esp_sntp_setservername(0, "pool.ntp.org"); esp_sntp_init();