mod: Phase 2a - add mod settings, I18n strings, and main.cpp integration
CrossPointSettings: Add mod-specific enums and fields: - Clock: CLOCK_FORMAT, CLOCK_SIZE, TIMEZONE, clockFormat, clockSize, timezone, timezoneOffsetHours, autoNtpSync - Sleep: SLEEP_SCREEN_LETTERBOX_FILL, sleepScreenLetterboxFill - Reader: preferredPortrait, preferredLandscape - Indexing: INDEXING_DISPLAY, indexingDisplay - getTimezonePosixStr() for POSIX TZ string generation main.cpp: Integrate mod initialization: - OPDS store loading, boot NTP sync, timezone application - Clock refresh loop (re-render on minute change) - RTC time logging on boot SettingsList.h: Add clock, timezone, and letterbox fill settings JsonSettingsIO.cpp: Handle int8_t timezoneOffsetHours separately I18n: Add ~80 mod string keys (english.yaml + regenerated I18nKeys.h) Made-with: Cursor
This commit is contained in:
@@ -341,3 +341,81 @@ STR_LINK: "[link]"
|
||||
STR_SCREENSHOT_BUTTON: "Take screenshot"
|
||||
STR_AUTO_TURN_ENABLED: "Auto Turn Enabled: "
|
||||
STR_AUTO_TURN_PAGES_PER_MIN: "Auto Turn (Pages Per Minute)"
|
||||
|
||||
STR_CAT_CLOCK: "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"
|
||||
STR_SYNC_CLOCK: "Sync Clock"
|
||||
STR_TIME_SYNCED: "Time synced!"
|
||||
STR_AUTO_NTP_SYNC: "Auto Sync on Boot"
|
||||
STR_LETTERBOX_FILL: "Letterbox Fill"
|
||||
STR_DITHERED: "Dithered"
|
||||
STR_SOLID: "Solid"
|
||||
STR_ADD_BOOKMARK: "Add Bookmark"
|
||||
STR_REMOVE_BOOKMARK: "Remove Bookmark"
|
||||
STR_LOOKUP_WORD: "Lookup Word"
|
||||
STR_LOOKUP_HISTORY: "Lookup Word History"
|
||||
STR_GO_TO_BOOKMARK: "Go to Bookmark"
|
||||
STR_CLOSE_BOOK: "Close Book"
|
||||
STR_DELETE_DICT_CACHE: "Delete Dictionary Cache"
|
||||
STR_DEFAULT_OPTION: "Default"
|
||||
STR_BOOKMARK_ADDED: "Bookmark added"
|
||||
STR_BOOKMARK_REMOVED: "Bookmark removed"
|
||||
STR_DICT_CACHE_DELETED: "Dictionary cache deleted"
|
||||
STR_NO_CACHE_TO_DELETE: "No cache to delete"
|
||||
STR_TABLE_OF_CONTENTS: "Table of Contents"
|
||||
STR_TOGGLE_ORIENTATION: "Toggle Portrait/Landscape"
|
||||
STR_TOGGLE_FONT_SIZE: "Toggle Font Size"
|
||||
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_INDEXING_DISPLAY: "Indexing Display"
|
||||
STR_INDEXING_POPUP: "Popup"
|
||||
STR_INDEXING_STATUS_TEXT: "Status Bar Text"
|
||||
STR_INDEXING_STATUS_ICON: "Status Bar Icon"
|
||||
STR_MANAGE_BOOK: "Manage Book"
|
||||
STR_ARCHIVE_BOOK: "Archive Book"
|
||||
STR_UNARCHIVE_BOOK: "Unarchive Book"
|
||||
STR_DELETE_BOOK: "Delete Book"
|
||||
STR_DELETE_CACHE_ONLY: "Delete Cache Only"
|
||||
STR_REINDEX_BOOK: "Reindex Book"
|
||||
STR_BROWSE_ARCHIVE: "Browse Archive"
|
||||
STR_BOOK_ARCHIVED: "Book archived"
|
||||
STR_BOOK_UNARCHIVED: "Book unarchived"
|
||||
STR_BOOK_DELETED: "Book deleted"
|
||||
STR_CACHE_DELETED: "Cache deleted"
|
||||
STR_BOOK_REINDEXED: "Book reindexed"
|
||||
STR_ACTION_FAILED: "Action failed"
|
||||
STR_BACK_TO_BEGINNING: "Back to Beginning"
|
||||
STR_CLOSE_MENU: "Close Menu"
|
||||
STR_ADD_SERVER: "Add Server"
|
||||
STR_SERVER_NAME: "Server Name"
|
||||
STR_NO_SERVERS: "No OPDS servers configured"
|
||||
STR_DELETE_SERVER: "Delete Server"
|
||||
STR_DELETE_CONFIRM: "Delete this server?"
|
||||
STR_OPDS_SERVERS: "OPDS Servers"
|
||||
STR_SAVE_HERE: "Save Here"
|
||||
STR_SELECT_FOLDER: "Select Folder"
|
||||
STR_DOWNLOAD_PATH: "Download Path"
|
||||
STR_POSITION: "Position"
|
||||
STR_DOWNLOAD_COMPLETE: "Download Complete!"
|
||||
STR_OPEN_BOOK: "Open Book"
|
||||
STR_BACK_TO_LISTING: "Back to Listing"
|
||||
STR_AFTER_DOWNLOAD: "After Download"
|
||||
|
||||
@@ -334,3 +334,29 @@ int CrossPointSettings::getReaderFontId() const {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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: {
|
||||
static char buf[16];
|
||||
int posixOffset = -timezoneOffsetHours;
|
||||
snprintf(buf, sizeof(buf), "UTC%d", posixOffset);
|
||||
return buf;
|
||||
}
|
||||
case TZ_UTC:
|
||||
default:
|
||||
return "UTC0";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,6 +137,41 @@ class CrossPointSettings {
|
||||
// Image rendering in EPUB reader
|
||||
enum IMAGE_RENDERING { IMAGES_DISPLAY = 0, IMAGES_PLACEHOLDER = 1, IMAGES_SUPPRESS = 2, IMAGE_RENDERING_COUNT };
|
||||
|
||||
// Sleep screen letterbox fill mode
|
||||
enum SLEEP_SCREEN_LETTERBOX_FILL {
|
||||
LETTERBOX_DITHERED = 0,
|
||||
LETTERBOX_SOLID = 1,
|
||||
LETTERBOX_NONE = 2,
|
||||
SLEEP_SCREEN_LETTERBOX_FILL_COUNT
|
||||
};
|
||||
|
||||
// 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
|
||||
};
|
||||
|
||||
// Indexing feedback display mode
|
||||
enum INDEXING_DISPLAY {
|
||||
INDEXING_POPUP = 0,
|
||||
INDEXING_STATUS_TEXT = 1,
|
||||
INDEXING_STATUS_ICON = 2,
|
||||
INDEXING_DISPLAY_COUNT
|
||||
};
|
||||
|
||||
// Sleep screen settings
|
||||
uint8_t sleepScreen = DARK;
|
||||
// Sleep screen cover mode settings
|
||||
@@ -200,6 +235,26 @@ class CrossPointSettings {
|
||||
// Image rendering mode in EPUB reader
|
||||
uint8_t imageRendering = IMAGES_DISPLAY;
|
||||
|
||||
// --- Mod-specific settings ---
|
||||
|
||||
// Sleep screen letterbox fill mode (Dithered / Solid / None)
|
||||
uint8_t sleepScreenLetterboxFill = LETTERBOX_DITHERED;
|
||||
// Indexing feedback display mode
|
||||
uint8_t indexingDisplay = INDEXING_POPUP;
|
||||
// Preferred orientations for the portrait/landscape toggle
|
||||
uint8_t preferredPortrait = PORTRAIT;
|
||||
uint8_t preferredLandscape = LANDSCAPE_CW;
|
||||
// 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;
|
||||
// Automatically sync time via NTP on boot
|
||||
uint8_t autoNtpSync = 0;
|
||||
|
||||
~CrossPointSettings() = default;
|
||||
|
||||
// Get singleton instance
|
||||
@@ -225,6 +280,7 @@ class CrossPointSettings {
|
||||
float getReaderLineCompression() const;
|
||||
unsigned long getSleepTimeoutMs() const;
|
||||
int getRefreshFrequency() const;
|
||||
const char* getTimezonePosixStr() const;
|
||||
};
|
||||
|
||||
// Helper macro to access settings
|
||||
|
||||
@@ -121,6 +121,9 @@ bool JsonSettingsIO::saveSettings(const CrossPointSettings& s, const char* path)
|
||||
doc["frontButtonLeft"] = s.frontButtonLeft;
|
||||
doc["frontButtonRight"] = s.frontButtonRight;
|
||||
|
||||
// Mod: timezone offset is int8_t, not uint8_t — handled separately
|
||||
doc["timezoneOffsetHours"] = s.timezoneOffsetHours;
|
||||
|
||||
String json;
|
||||
serializeJson(doc, json);
|
||||
return Storage.writeFile(path, json);
|
||||
@@ -200,6 +203,11 @@ bool JsonSettingsIO::loadSettings(CrossPointSettings& s, const char* json, bool*
|
||||
clamp(doc["frontButtonRight"] | (uint8_t)S::FRONT_HW_RIGHT, S::FRONT_BUTTON_HARDWARE_COUNT, S::FRONT_HW_RIGHT);
|
||||
CrossPointSettings::validateFrontButtonMapping(s);
|
||||
|
||||
// Mod: timezone offset is int8_t, not uint8_t
|
||||
s.timezoneOffsetHours = doc["timezoneOffsetHours"] | (int8_t)0;
|
||||
if (s.timezoneOffsetHours < -12) s.timezoneOffsetHours = -12;
|
||||
if (s.timezoneOffsetHours > 14) s.timezoneOffsetHours = 14;
|
||||
|
||||
LOG_DBG("CPS", "Settings loaded from file");
|
||||
|
||||
return true;
|
||||
|
||||
@@ -118,6 +118,25 @@ inline const std::vector<SettingInfo>& getSettingsList() {
|
||||
SettingInfo::String(StrId::STR_PASSWORD, SETTINGS.opdsPassword, sizeof(SETTINGS.opdsPassword), "opdsPassword",
|
||||
StrId::STR_OPDS_BROWSER)
|
||||
.withObfuscated(),
|
||||
|
||||
// --- Mod: Clock ---
|
||||
SettingInfo::Enum(StrId::STR_CLOCK, &CrossPointSettings::clockFormat,
|
||||
{StrId::STR_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),
|
||||
SettingInfo::Toggle(StrId::STR_AUTO_NTP_SYNC, &CrossPointSettings::autoNtpSync, "autoNtpSync",
|
||||
StrId::STR_CAT_CLOCK),
|
||||
|
||||
// --- Mod: Sleep Screen ---
|
||||
SettingInfo::Enum(StrId::STR_LETTERBOX_FILL, &CrossPointSettings::sleepScreenLetterboxFill,
|
||||
{StrId::STR_DITHERED, StrId::STR_SOLID, StrId::STR_NONE_OPT}, "sleepScreenLetterboxFill",
|
||||
StrId::STR_CAT_DISPLAY),
|
||||
// --- Status Bar Settings (web-only, uses StatusBarSettingsActivity) ---
|
||||
SettingInfo::Toggle(StrId::STR_CHAPTER_PAGE_COUNT, &CrossPointSettings::statusBarChapterPageCount,
|
||||
"statusBarChapterPageCount", StrId::STR_CUSTOMISE_STATUS_BAR),
|
||||
|
||||
45
src/main.cpp
45
src/main.cpp
@@ -12,17 +12,21 @@
|
||||
#include <SPI.h>
|
||||
#include <builtinFonts/all.h>
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
|
||||
#include "CrossPointSettings.h"
|
||||
#include "CrossPointState.h"
|
||||
#include "KOReaderCredentialStore.h"
|
||||
#include "MappedInputManager.h"
|
||||
#include "OpdsServerStore.h"
|
||||
#include "RecentBooksStore.h"
|
||||
#include "activities/Activity.h"
|
||||
#include "activities/ActivityManager.h"
|
||||
#include "components/UITheme.h"
|
||||
#include "fontIds.h"
|
||||
#include "util/BootNtpSync.h"
|
||||
#include "util/ButtonNavigator.h"
|
||||
#include "util/ScreenshotUtil.h"
|
||||
|
||||
@@ -255,8 +259,15 @@ void setup() {
|
||||
HalSystem::clearPanic(); // TODO: move this to an activity when we have one to display the panic info
|
||||
|
||||
SETTINGS.loadFromFile();
|
||||
|
||||
// Apply saved timezone setting on boot
|
||||
setenv("TZ", SETTINGS.getTimezonePosixStr(), 1);
|
||||
tzset();
|
||||
|
||||
I18N.loadSettings();
|
||||
KOREADER_STORE.loadFromFile();
|
||||
OPDS_STORE.loadFromFile();
|
||||
BootNtpSync::start();
|
||||
UITheme::getInstance().reload();
|
||||
ButtonNavigator::setMappedInputManager(mappedInputManager);
|
||||
|
||||
@@ -281,6 +292,18 @@ void setup() {
|
||||
// First serial output only here to avoid timing inconsistencies for power button press duration verification
|
||||
LOG_DBG("MAIN", "Starting CrossPoint version " CROSSPOINT_VERSION);
|
||||
|
||||
// Log RTC time on boot for debugging
|
||||
{
|
||||
time_t now = time(nullptr);
|
||||
struct tm* t = localtime(&now);
|
||||
if (t != nullptr && t->tm_year > 100) {
|
||||
LOG_DBG("MAIN", "RTC time: %04d-%02d-%02d %02d:%02d:%02d", t->tm_year + 1900, t->tm_mon + 1, t->tm_mday,
|
||||
t->tm_hour, t->tm_min, t->tm_sec);
|
||||
} else {
|
||||
LOG_DBG("MAIN", "RTC time not set (epoch)");
|
||||
}
|
||||
}
|
||||
|
||||
setupDisplayAndFonts();
|
||||
|
||||
activityManager.goToBoot();
|
||||
@@ -376,6 +399,28 @@ void loop() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Refresh screen when the displayed minute changes (clock in header)
|
||||
if (SETTINGS.clockFormat != CrossPointSettings::CLOCK_OFF) {
|
||||
static int lastRenderedMinute = -1;
|
||||
static bool sawInvalidTime = false;
|
||||
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) {
|
||||
lastRenderedMinute = currentMinute;
|
||||
if (sawInvalidTime) {
|
||||
activityManager.requestUpdate();
|
||||
}
|
||||
} else if (currentMinute != lastRenderedMinute) {
|
||||
activityManager.requestUpdate();
|
||||
lastRenderedMinute = currentMinute;
|
||||
}
|
||||
} else {
|
||||
sawInvalidTime = true;
|
||||
}
|
||||
}
|
||||
|
||||
const unsigned long activityStartTime = millis();
|
||||
activityManager.loop();
|
||||
const unsigned long activityDuration = millis() - activityStartTime;
|
||||
|
||||
Reference in New Issue
Block a user