Files
crosspoint-reader-mod/src/activities/settings/SetTimeActivity.cpp
cottongin dfbc931c14 mod: Phase 1 - bring forward mod-exclusive files with ActivityManager migration
Brings ~55 mod-exclusive files to the upstream-based mod/master-resync branch:

Activities (migrated to new ActivityManager pattern):
- Clock/Time: SetTimeActivity, SetTimezoneOffsetActivity, NtpSyncActivity
- Dictionary: DictionaryDefinitionActivity, DictionarySuggestionsActivity,
  DictionaryWordSelectActivity, LookedUpWordsActivity
- Bookmark: EpubReaderBookmarkSelectionActivity
- Book management: BookManageMenuActivity, EndOfBookMenuActivity
- OPDS: OpdsServerListActivity, OpdsSettingsActivity
- Utility: DirectoryPickerActivity, NumericStepperActivity

Utilities (unchanged):
- BookManager, BookSettings, BookmarkStore, BootNtpSync
- Dictionary, LookupHistory, TimeSync, OpdsServerStore

Libraries: PlaceholderCover, TableData, ChapterXPathIndexer
Scripts: inject_mod_version, generate_book_icon, preview_placeholder_cover
Docs: KOReader sync XPath mapping

Migration changes:
- ActivityWithSubactivity -> Activity base class
- Callback constructors -> finish()/setResult() pattern
- enterNewActivity() -> startActivityForResult()
- Activity::RenderLock&& -> RenderLock&&

These files won't compile yet - they reference mod settings and I18n
strings that will be added in subsequent phases.

Made-with: Cursor
2026-03-07 15:10:00 -05:00

158 lines
4.7 KiB
C++

#include "SetTimeActivity.h"
#include <GfxRenderer.h>
#include <I18n.h>
#include <sys/time.h>
#include <cstdio>
#include <ctime>
#include "CrossPointSettings.h"
#include "MappedInputManager.h"
#include "components/UITheme.h"
#include "fontIds.h"
void SetTimeActivity::onEnter() {
Activity::onEnter();
// Initialize from current system time if it's been set (year > 2000)
time_t now = time(nullptr);
struct tm* t = localtime(&now);
if (t != nullptr && t->tm_year > 100) {
hour = t->tm_hour;
minute = t->tm_min;
} else {
hour = 12;
minute = 0;
}
selectedField = 0;
requestUpdate();
}
void SetTimeActivity::onExit() { Activity::onExit(); }
void SetTimeActivity::loop() {
// Back button: discard and exit
if (mappedInput.wasPressed(MappedInputManager::Button::Back)) {
finish();
return;
}
// Confirm button: apply time and exit
if (mappedInput.wasPressed(MappedInputManager::Button::Confirm)) {
applyTime();
finish();
return;
}
// Left/Right: switch between hour and minute fields
if (mappedInput.wasPressed(MappedInputManager::Button::Left)) {
selectedField = 0;
requestUpdate();
return;
}
if (mappedInput.wasPressed(MappedInputManager::Button::Right)) {
selectedField = 1;
requestUpdate();
return;
}
// Up/Down: increment/decrement the selected field
if (mappedInput.wasPressed(MappedInputManager::Button::Up)) {
if (selectedField == 0) {
hour = (hour + 1) % 24;
} else {
minute = (minute + 1) % 60;
}
requestUpdate();
return;
}
if (mappedInput.wasPressed(MappedInputManager::Button::Down)) {
if (selectedField == 0) {
hour = (hour + 23) % 24;
} else {
minute = (minute + 59) % 60;
}
requestUpdate();
return;
}
}
void SetTimeActivity::render(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_TIME), true, EpdFontFamily::BOLD);
// Format hour and minute strings
char hourStr[4];
char minuteStr[4];
snprintf(hourStr, sizeof(hourStr), "%02d", hour);
snprintf(minuteStr, sizeof(minuteStr), "%02d", minute);
const int colonWidth = renderer.getTextWidth(UI_12_FONT_ID, " : ");
const int digitWidth = renderer.getTextWidth(UI_12_FONT_ID, "00");
const int totalWidth = digitWidth * 2 + colonWidth;
const int startX = (pageWidth - totalWidth) / 2;
const int timeY = 80;
// Draw selection highlight behind the selected field
constexpr int highlightPad = 6;
if (selectedField == 0) {
renderer.fillRoundedRect(startX - highlightPad, timeY - 4, digitWidth + highlightPad * 2, lineHeight12 + 8, 6,
Color::LightGray);
} else {
renderer.fillRoundedRect(startX + digitWidth + colonWidth - highlightPad, timeY - 4, digitWidth + highlightPad * 2,
lineHeight12 + 8, 6, Color::LightGray);
}
// Draw the time digits and colon
renderer.drawText(UI_12_FONT_ID, startX, timeY, hourStr, true);
renderer.drawText(UI_12_FONT_ID, startX + digitWidth, timeY, " : ", true);
renderer.drawText(UI_12_FONT_ID, startX + digitWidth + colonWidth, timeY, minuteStr, true);
// Draw up/down arrows above and below the selected field
const int arrowX = (selectedField == 0) ? startX + digitWidth / 2 : startX + digitWidth + colonWidth + digitWidth / 2;
const int arrowUpY = timeY - 20;
const int arrowDownY = timeY + lineHeight12 + 12;
// Up arrow (simple triangle using lines)
constexpr int arrowSize = 6;
for (int row = 0; row < arrowSize; row++) {
renderer.drawLine(arrowX - row, arrowUpY + row, arrowX + row, arrowUpY + row);
}
// Down arrow
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();
}
void SetTimeActivity::applyTime() {
time_t now = time(nullptr);
struct tm newTime = {};
struct tm* current = localtime(&now);
if (current != nullptr && current->tm_year > 100) {
newTime = *current;
} else {
// If time was never set, use a reasonable date (2025-01-01)
newTime.tm_year = 125; // years since 1900
newTime.tm_mon = 0;
newTime.tm_mday = 1;
}
newTime.tm_hour = hour;
newTime.tm_min = minute;
newTime.tm_sec = 0;
time_t newEpoch = mktime(&newTime);
struct timeval tv = {.tv_sec = newEpoch, .tv_usec = 0};
settimeofday(&tv, nullptr);
}