#include "settings_menu.h" #include "choices.h" namespace roro { namespace { using Row = SettingsMenu::Row; using Kind = SettingsMenu::Kind; struct RowDef { Row row; Kind kind; const char* label; }; const RowDef kRows[] = { {Row::LongName, Kind::Text, "Long name"}, {Row::ShortName, Kind::Text, "Short name"}, {Row::Region, Kind::Choice, "Region"}, {Row::Timezone, Kind::Choice, "Timezone"}, {Row::Brightness, Kind::Slider, "Brightness"}, {Row::DimTimeout, Kind::Choice, "Dim after"}, {Row::OffTimeout, Kind::Choice, "Screen off after"}, {Row::Sound, Kind::Toggle, "Sound & LED"}, {Row::Gnss, Kind::Toggle, "GNSS"}, {Row::GnssQuiet, Kind::Toggle, "Pause GNSS for LoRa"}, {Row::Coordinates, Kind::Toggle, "Coordinates"}, {Row::ProbeMacs, Kind::Toggle, "Probe MACs"}, {Row::Wifi, Kind::Page, "Wi-Fi"}, {Row::CheckUpdates, Kind::Toggle, "Check for updates"}, {Row::Firmware, Kind::Page, "Firmware"}, {Row::About, Kind::Page, "About"}, }; const int kDimSeconds[] = {10, 15, 30, 60, 120, 300}; const int kOffSeconds[] = {30, 60, 120, 300, 600, 1800}; std::string formatSeconds(int s) { return s < 60 ? std::to_string(s) + " s" : std::to_string(s / 60) + " min"; } template std::vector labels(const Choice (&table)[N]) { std::vector out; for (auto& c : table) out.push_back(c.label); return out; } template std::vector durations(const int (&seconds)[N]) { std::vector out; for (int s : seconds) out.push_back(formatSeconds(s)); return out; } template int indexOf(const Choice (&table)[N], const std::string& value) { for (size_t i = 0; i < N; i++) if (value == table[i].value) return static_cast(i); return -1; } template int indexOf(const int (&table)[N], int value) { for (size_t i = 0; i < N; i++) if (table[i] == value) return static_cast(i); return -1; } } // namespace int SettingsMenu::count() const { return sizeof(kRows) / sizeof(kRows[0]); } SettingsMenu::Row SettingsMenu::row(int i) const { return kRows[i].row; } SettingsMenu::Kind SettingsMenu::kind(int i) const { return kRows[i].kind; } std::string SettingsMenu::label(int i) const { return kRows[i].label; } std::string SettingsMenu::value(int i) const { switch (row(i)) { case Row::LongName: return settings_.getString(Setting::LongName); case Row::ShortName: return settings_.getString(Setting::ShortName); case Row::Region: return settings_.getString(Setting::Region); case Row::Timezone: { int c = currentChoice(i); return c >= 0 ? kTimezones[c].label : "Custom"; } case Row::Brightness: return std::to_string(settings_.getInt(Setting::Brightness)) + "%"; case Row::DimTimeout: return formatSeconds(settings_.getInt(Setting::DimTimeoutS)); case Row::OffTimeout: return formatSeconds(settings_.getInt(Setting::OffTimeoutS)); case Row::Sound: return settings_.getBool(Setting::Sound) ? "On" : "Off"; case Row::Gnss: return settings_.getBool(Setting::GnssEnabled) ? "On" : "Off"; case Row::GnssQuiet: return settings_.getBool(Setting::GnssQuietForLora) ? "On" : "Off"; case Row::CheckUpdates: return settings_.getBool(Setting::CheckUpdates) ? "Daily" : "Off"; case Row::Coordinates: return settings_.getBool(Setting::CoordinatesDms) ? "Deg min sec" : "Decimal"; case Row::ProbeMacs: return settings_.getBool(Setting::ProbeMacRaw) ? "Raw" : "Pseudonymised"; case Row::Wifi: return settings_.getBool(Setting::WifiEnabled) ? "On" : "Off"; default: return ""; } } std::vector SettingsMenu::choices(int i) const { switch (row(i)) { case Row::Region: return labels(kRegions); case Row::Timezone: return labels(kTimezones); case Row::DimTimeout: return durations(kDimSeconds); case Row::OffTimeout: return durations(kOffSeconds); default: return {}; } } int SettingsMenu::currentChoice(int i) const { switch (row(i)) { case Row::Region: return indexOf(kRegions, settings_.getString(Setting::Region)); case Row::Timezone: return indexOf(kTimezones, settings_.getString(Setting::Timezone)); case Row::DimTimeout: return indexOf(kDimSeconds, settings_.getInt(Setting::DimTimeoutS)); case Row::OffTimeout: return indexOf(kOffSeconds, settings_.getInt(Setting::OffTimeoutS)); default: return -1; } } std::string SettingsMenu::choose(int i, int c) { switch (row(i)) { case Row::Region: settings_.setString(Setting::Region, kRegions[c].value); settings_.setBool(Setting::RegionConfirmed, true); return ""; case Row::Timezone: settings_.setString(Setting::Timezone, kTimezones[c].value); return ""; case Row::DimTimeout: return settings_.setInt(Setting::DimTimeoutS, kDimSeconds[c]) ? "" : "Dimming must happen before screen off"; case Row::OffTimeout: return settings_.setInt(Setting::OffTimeoutS, kOffSeconds[c]) ? "" : "Screen off must come after dimming"; default: return "Not a choice"; } } void SettingsMenu::toggle(int i) { if (row(i) == Row::Sound) settings_.setBool(Setting::Sound, !settings_.getBool(Setting::Sound)); if (row(i) == Row::Gnss) settings_.setBool(Setting::GnssEnabled, !settings_.getBool(Setting::GnssEnabled)); if (row(i) == Row::GnssQuiet) settings_.setBool(Setting::GnssQuietForLora, !settings_.getBool(Setting::GnssQuietForLora)); if (row(i) == Row::CheckUpdates) settings_.setBool(Setting::CheckUpdates, !settings_.getBool(Setting::CheckUpdates)); if (row(i) == Row::Coordinates) settings_.setBool(Setting::CoordinatesDms, !settings_.getBool(Setting::CoordinatesDms)); if (row(i) == Row::ProbeMacs) settings_.setBool(Setting::ProbeMacRaw, !settings_.getBool(Setting::ProbeMacRaw)); } void SettingsMenu::adjust(int i, int direction) { if (row(i) != Row::Brightness) return; int next = settings_.getInt(Setting::Brightness) + (direction > 0 ? 10 : -10); if (next < 10) next = 10; if (next > 100) next = 100; settings_.setInt(Setting::Brightness, next); } int SettingsMenu::maxBytes(int i) const { return row(i) == Row::ShortName ? 4 : row(i) == Row::LongName ? 39 : 0; } std::string SettingsMenu::setText(int i, const std::string& text) { Setting s = row(i) == Row::ShortName ? Setting::ShortName : Setting::LongName; if (settings_.setString(s, text)) return ""; return "Must be 1 to " + std::to_string(maxBytes(i)) + " bytes"; } } // namespace roro