From 513715e472a4576938aef70c7f7352ecfa3a5f16 Mon Sep 17 00:00:00 2001 From: pelgraine <140762863+pelgraine@users.noreply.github.com> Date: Sun, 1 Mar 2026 14:11:39 +1100 Subject: [PATCH] add contacts settings in settings --- .../companion_radio/ui-new/Settingsscreen.h | 298 +++++++++++++++--- 1 file changed, 247 insertions(+), 51 deletions(-) diff --git a/examples/companion_radio/ui-new/Settingsscreen.h b/examples/companion_radio/ui-new/Settingsscreen.h index 1e98a3b7..3ed56af5 100644 --- a/examples/companion_radio/ui-new/Settingsscreen.h +++ b/examples/companion_radio/ui-new/Settingsscreen.h @@ -15,6 +15,24 @@ class UITask; class MyMesh; extern MyMesh the_mesh; +// --------------------------------------------------------------------------- +// Auto-add config bitmask (mirrored from MyMesh.cpp for UI access) +// --------------------------------------------------------------------------- +#define AUTO_ADD_OVERWRITE_OLDEST (1 << 0) // 0x01 - overwrite oldest non-favourite when full +#define AUTO_ADD_CHAT (1 << 1) // 0x02 - auto-add Chat (Companion) (ADV_TYPE_CHAT) +#define AUTO_ADD_REPEATER (1 << 2) // 0x04 - auto-add Repeater (ADV_TYPE_REPEATER) +#define AUTO_ADD_ROOM_SERVER (1 << 3) // 0x08 - auto-add Room Server (ADV_TYPE_ROOM) +#define AUTO_ADD_SENSOR (1 << 4) // 0x10 - auto-add Sensor (ADV_TYPE_SENSOR) + +// All type bits combined (excludes overwrite flag) +#define AUTO_ADD_ALL_TYPES (AUTO_ADD_CHAT | AUTO_ADD_REPEATER | AUTO_ADD_ROOM_SERVER | AUTO_ADD_SENSOR) + +// Contact mode indices for picker +#define CONTACT_MODE_AUTO_ALL 0 // Add all contacts automatically +#define CONTACT_MODE_CUSTOM 1 // Per-type toggles +#define CONTACT_MODE_MANUAL 2 // No auto-add, companion app only +#define CONTACT_MODE_COUNT 3 + // --------------------------------------------------------------------------- // Radio presets // --------------------------------------------------------------------------- @@ -51,28 +69,35 @@ static const RadioPreset RADIO_PRESETS[] = { // Settings row types // --------------------------------------------------------------------------- enum SettingsRowType : uint8_t { - ROW_NAME, // Device name (text editor) - ROW_RADIO_PRESET, // Radio preset picker - ROW_FREQ, // Frequency (float) - ROW_BW, // Bandwidth (float) - ROW_SF, // Spreading factor (5-12) - ROW_CR, // Coding rate (5-8) - ROW_TX_POWER, // TX power (1-20 dBm) - ROW_UTC_OFFSET, // UTC offset (-12 to +14) - ROW_MSG_NOTIFY, // Keyboard flash on new msg toggle + ROW_NAME, // Device name (text editor) + ROW_RADIO_PRESET, // Radio preset picker + ROW_FREQ, // Frequency (float) + ROW_BW, // Bandwidth (float) + ROW_SF, // Spreading factor (5-12) + ROW_CR, // Coding rate (5-8) + ROW_TX_POWER, // TX power (1-20 dBm) + ROW_UTC_OFFSET, // UTC offset (-12 to +14) + ROW_MSG_NOTIFY, // Keyboard flash on new msg toggle #ifdef HAS_4G_MODEM - ROW_MODEM_TOGGLE, // 4G modem enable/disable toggle (4G builds only) + ROW_MODEM_TOGGLE, // 4G modem enable/disable toggle (4G builds only) #endif - ROW_CH_HEADER, // "--- Channels ---" separator - ROW_CHANNEL, // A channel entry (dynamic, index stored separately) - ROW_ADD_CHANNEL, // "+ Add Hashtag Channel" - ROW_INFO_HEADER, // "--- Info ---" separator - ROW_PUB_KEY, // Public key display - ROW_FIRMWARE, // Firmware version + ROW_CONTACT_HEADER, // "--- Contacts ---" separator + ROW_CONTACT_MODE, // Contact auto-add mode picker (Auto All / Custom / Manual) + ROW_AUTOADD_CHAT, // Toggle: auto-add Chat clients + ROW_AUTOADD_REPEATER, // Toggle: auto-add Repeaters + ROW_AUTOADD_ROOM, // Toggle: auto-add Room Servers + ROW_AUTOADD_SENSOR, // Toggle: auto-add Sensors + ROW_AUTOADD_OVERWRITE, // Toggle: overwrite oldest non-favourite when full + ROW_CH_HEADER, // "--- Channels ---" separator + ROW_CHANNEL, // A channel entry (dynamic, index stored separately) + ROW_ADD_CHANNEL, // "+ Add Hashtag Channel" + ROW_INFO_HEADER, // "--- Info ---" separator + ROW_PUB_KEY, // Public key display + ROW_FIRMWARE, // Firmware version #ifdef HAS_4G_MODEM - ROW_IMEI, // IMEI display (read-only) - ROW_OPERATOR_INFO, // Carrier/operator display (read-only) - ROW_APN, // APN setting (editable) + ROW_IMEI, // IMEI display (read-only) + ROW_OPERATOR_INFO, // Carrier/operator display (read-only) + ROW_APN, // APN setting (editable) #endif }; @@ -82,16 +107,16 @@ enum SettingsRowType : uint8_t { enum EditMode : uint8_t { EDIT_NONE, // Just browsing EDIT_TEXT, // Typing into a text buffer (name, channel name) - EDIT_PICKER, // A/D cycles options (radio preset) + EDIT_PICKER, // A/D cycles options (radio preset, contact mode) EDIT_NUMBER, // W/S adjusts value (freq, BW, SF, CR, TX, UTC) EDIT_CONFIRM, // Confirmation dialog (delete channel, apply radio) }; -// Max rows in the settings list +// Max rows in the settings list (increased for contact sub-toggles) #ifdef HAS_4G_MODEM -#define SETTINGS_MAX_ROWS 46 // Extra rows for IMEI, Carrier, APN +#define SETTINGS_MAX_ROWS 54 // Extra rows for IMEI, Carrier, APN + contacts #else -#define SETTINGS_MAX_ROWS 40 +#define SETTINGS_MAX_ROWS 48 #endif #define SETTINGS_TEXT_BUF 33 // 32 chars + null @@ -101,7 +126,7 @@ private: mesh::RTCClock* _rtc; NodePrefs* _prefs; - // Row table — rebuilt whenever channels change + // Row table — rebuilt whenever channels or contact mode change struct Row { SettingsRowType type; uint8_t param; // channel index for ROW_CHANNEL, preset index for ROW_RADIO_PRESET @@ -117,7 +142,7 @@ private: EditMode _editMode; char _editBuf[SETTINGS_TEXT_BUF]; int _editPos; - int _editPickerIdx; // for preset picker + int _editPickerIdx; // for preset picker / contact mode picker float _editFloat; // for freq/BW editing int _editInt; // for SF/CR/TX/UTC editing int _confirmAction; // 0=none, 1=delete channel, 2=apply radio @@ -125,7 +150,7 @@ private: // Onboarding mode bool _onboarding; - // Dirty flag for radio params — prompt to apply + // Dirty flag for radio params — prompt to apply bool _radioChanged; // 4G modem state (runtime cache of config) @@ -133,6 +158,57 @@ private: bool _modemEnabled; #endif + // --------------------------------------------------------------------------- + // Contact mode helpers + // --------------------------------------------------------------------------- + + // Determine current contact mode from prefs + int getContactMode() const { + if ((_prefs->manual_add_contacts & 1) == 0) { + return CONTACT_MODE_AUTO_ALL; + } + // manual_add_contacts bit 0 is set — check if any type bits are enabled + if ((_prefs->autoadd_config & AUTO_ADD_ALL_TYPES) != 0) { + return CONTACT_MODE_CUSTOM; + } + return CONTACT_MODE_MANUAL; + } + + // Get display label for a contact mode + static const char* contactModeLabel(int mode) { + switch (mode) { + case CONTACT_MODE_AUTO_ALL: return "Auto All"; + case CONTACT_MODE_CUSTOM: return "Custom"; + case CONTACT_MODE_MANUAL: return "Manual Only"; + default: return "?"; + } + } + + // Apply a contact mode selection from picker + void applyContactMode(int mode) { + switch (mode) { + case CONTACT_MODE_AUTO_ALL: + _prefs->manual_add_contacts &= ~1; // clear bit 0 → auto all + break; + case CONTACT_MODE_CUSTOM: + _prefs->manual_add_contacts |= 1; // set bit 0 → selective + // If no type bits are set, default to all types enabled + if ((_prefs->autoadd_config & AUTO_ADD_ALL_TYPES) == 0) { + _prefs->autoadd_config |= AUTO_ADD_ALL_TYPES; + } + break; + case CONTACT_MODE_MANUAL: + _prefs->manual_add_contacts |= 1; // set bit 0 → selective + _prefs->autoadd_config &= ~AUTO_ADD_ALL_TYPES; // clear all type bits + // Note: keeps AUTO_ADD_OVERWRITE_OLDEST bit unchanged + break; + } + the_mesh.savePrefs(); + rebuildRows(); // show/hide sub-toggles + Serial.printf("Settings: Contact mode = %s (manual=%d, autoadd=0x%02X)\n", + contactModeLabel(mode), _prefs->manual_add_contacts, _prefs->autoadd_config); + } + // --------------------------------------------------------------------------- // Row table management // --------------------------------------------------------------------------- @@ -152,6 +228,21 @@ private: #ifdef HAS_4G_MODEM addRow(ROW_MODEM_TOGGLE); #endif + + // --- Contacts section --- + addRow(ROW_CONTACT_HEADER); + addRow(ROW_CONTACT_MODE); + + // Show per-type sub-toggles only in Custom mode + if (getContactMode() == CONTACT_MODE_CUSTOM) { + addRow(ROW_AUTOADD_CHAT); + addRow(ROW_AUTOADD_REPEATER); + addRow(ROW_AUTOADD_ROOM); + addRow(ROW_AUTOADD_SENSOR); + addRow(ROW_AUTOADD_OVERWRITE); + } + + // --- Channels section --- addRow(ROW_CH_HEADER); // Enumerate current channels @@ -192,7 +283,7 @@ private: bool isSelectable(int idx) const { if (idx < 0 || idx >= _numRows) return false; SettingsRowType t = _rows[idx].type; - return t != ROW_CH_HEADER && t != ROW_INFO_HEADER + return t != ROW_CH_HEADER && t != ROW_INFO_HEADER && t != ROW_CONTACT_HEADER #ifdef HAS_4G_MODEM && t != ROW_IMEI && t != ROW_OPERATOR_INFO #endif @@ -246,11 +337,11 @@ private: strncpy(newCh.name, chanName, sizeof(newCh.name)); newCh.name[31] = '\0'; - // SHA-256 the channel name → first 16 bytes become the secret + // SHA-256 the channel name → first 16 bytes become the secret uint8_t hash[32]; mesh::Utils::sha256(hash, 32, (const uint8_t*)chanName, strlen(chanName)); memcpy(newCh.channel.secret, hash, 16); - // Upper 16 bytes left as zero → setChannel uses 128-bit mode + // Upper 16 bytes left as zero → setChannel uses 128-bit mode // Find next empty slot for (uint8_t i = 0; i < MAX_GROUP_CHANNELS; i++) { @@ -518,6 +609,54 @@ public: break; #endif + // --- Contacts section --- + case ROW_CONTACT_HEADER: + display.setColor(DisplayDriver::YELLOW); + display.print("--- Contacts ---"); + break; + + case ROW_CONTACT_MODE: + if (editing && _editMode == EDIT_PICKER) { + snprintf(tmp, sizeof(tmp), "< Add Mode: %s >", + contactModeLabel(_editPickerIdx)); + } else { + snprintf(tmp, sizeof(tmp), "Add Mode: %s", + contactModeLabel(getContactMode())); + } + display.print(tmp); + break; + + case ROW_AUTOADD_CHAT: + snprintf(tmp, sizeof(tmp), " Chat: %s", + (_prefs->autoadd_config & AUTO_ADD_CHAT) ? "ON" : "OFF"); + display.print(tmp); + break; + + case ROW_AUTOADD_REPEATER: + snprintf(tmp, sizeof(tmp), " Repeater: %s", + (_prefs->autoadd_config & AUTO_ADD_REPEATER) ? "ON" : "OFF"); + display.print(tmp); + break; + + case ROW_AUTOADD_ROOM: + snprintf(tmp, sizeof(tmp), " Room Server: %s", + (_prefs->autoadd_config & AUTO_ADD_ROOM_SERVER) ? "ON" : "OFF"); + display.print(tmp); + break; + + case ROW_AUTOADD_SENSOR: + snprintf(tmp, sizeof(tmp), " Sensor: %s", + (_prefs->autoadd_config & AUTO_ADD_SENSOR) ? "ON" : "OFF"); + display.print(tmp); + break; + + case ROW_AUTOADD_OVERWRITE: + snprintf(tmp, sizeof(tmp), " Overwrite Oldest: %s", + (_prefs->autoadd_config & AUTO_ADD_OVERWRITE_OLDEST) ? "ON" : "OFF"); + display.print(tmp); + break; + + // --- Channels section --- case ROW_CH_HEADER: display.setColor(DisplayDriver::YELLOW); display.print("--- Channels ---"); @@ -609,7 +748,7 @@ public: char apnShort[29]; strncpy(apnShort, apn, 28); apnShort[28] = '\0'; - // Abbreviate source: auto→A, network→N, user→U, none→? + // Abbreviate source: auto->A, network->N, user->U, none->? char srcChar = '?'; if (strcmp(src, "auto") == 0) srcChar = 'A'; else if (strcmp(src, "network") == 0) srcChar = 'N'; @@ -775,34 +914,53 @@ public: return true; // consume all keys in text edit } - // --- Picker mode (radio preset) --- + // --- Picker mode (radio preset or contact mode) --- if (_editMode == EDIT_PICKER) { + SettingsRowType type = _rows[_cursor].type; + if (c == 'a' || c == 'A') { - _editPickerIdx--; - if (_editPickerIdx < 0) _editPickerIdx = (int)NUM_RADIO_PRESETS - 1; + if (type == ROW_CONTACT_MODE) { + _editPickerIdx--; + if (_editPickerIdx < 0) _editPickerIdx = CONTACT_MODE_COUNT - 1; + } else { + // Radio preset + _editPickerIdx--; + if (_editPickerIdx < 0) _editPickerIdx = (int)NUM_RADIO_PRESETS - 1; + } return true; } if (c == 'd' || c == 'D') { - _editPickerIdx++; - if (_editPickerIdx >= (int)NUM_RADIO_PRESETS) _editPickerIdx = 0; + if (type == ROW_CONTACT_MODE) { + _editPickerIdx++; + if (_editPickerIdx >= CONTACT_MODE_COUNT) _editPickerIdx = 0; + } else { + // Radio preset + _editPickerIdx++; + if (_editPickerIdx >= (int)NUM_RADIO_PRESETS) _editPickerIdx = 0; + } return true; } if (c == '\r' || c == 13) { - // Apply preset - if (_editPickerIdx >= 0 && _editPickerIdx < (int)NUM_RADIO_PRESETS) { - const RadioPreset& p = RADIO_PRESETS[_editPickerIdx]; - _prefs->freq = p.freq; - _prefs->bw = p.bw; - _prefs->sf = p.sf; - _prefs->cr = p.cr; - _prefs->tx_power_dbm = p.tx_power; - _radioChanged = true; - } - _editMode = EDIT_NONE; - if (_onboarding) { - // Apply and finish onboarding - applyRadioParams(); - _onboarding = false; + if (type == ROW_CONTACT_MODE) { + applyContactMode(_editPickerIdx); + _editMode = EDIT_NONE; + } else { + // Apply radio preset + if (_editPickerIdx >= 0 && _editPickerIdx < (int)NUM_RADIO_PRESETS) { + const RadioPreset& p = RADIO_PRESETS[_editPickerIdx]; + _prefs->freq = p.freq; + _prefs->bw = p.bw; + _prefs->sf = p.sf; + _prefs->cr = p.cr; + _prefs->tx_power_dbm = p.tx_power; + _radioChanged = true; + } + _editMode = EDIT_NONE; + if (_onboarding) { + // Apply and finish onboarding + applyRadioParams(); + _onboarding = false; + } } return true; } @@ -963,6 +1121,44 @@ public: break; } #endif + + // --- Contact mode picker --- + case ROW_CONTACT_MODE: + startEditPicker(getContactMode()); + break; + + // --- Contact sub-toggles (flip bit and save) --- + case ROW_AUTOADD_CHAT: + _prefs->autoadd_config ^= AUTO_ADD_CHAT; + the_mesh.savePrefs(); + Serial.printf("Settings: Auto-add Chat = %s\n", + (_prefs->autoadd_config & AUTO_ADD_CHAT) ? "ON" : "OFF"); + break; + case ROW_AUTOADD_REPEATER: + _prefs->autoadd_config ^= AUTO_ADD_REPEATER; + the_mesh.savePrefs(); + Serial.printf("Settings: Auto-add Repeater = %s\n", + (_prefs->autoadd_config & AUTO_ADD_REPEATER) ? "ON" : "OFF"); + break; + case ROW_AUTOADD_ROOM: + _prefs->autoadd_config ^= AUTO_ADD_ROOM_SERVER; + the_mesh.savePrefs(); + Serial.printf("Settings: Auto-add Room = %s\n", + (_prefs->autoadd_config & AUTO_ADD_ROOM_SERVER) ? "ON" : "OFF"); + break; + case ROW_AUTOADD_SENSOR: + _prefs->autoadd_config ^= AUTO_ADD_SENSOR; + the_mesh.savePrefs(); + Serial.printf("Settings: Auto-add Sensor = %s\n", + (_prefs->autoadd_config & AUTO_ADD_SENSOR) ? "ON" : "OFF"); + break; + case ROW_AUTOADD_OVERWRITE: + _prefs->autoadd_config ^= AUTO_ADD_OVERWRITE_OLDEST; + the_mesh.savePrefs(); + Serial.printf("Settings: Overwrite oldest = %s\n", + (_prefs->autoadd_config & AUTO_ADD_OVERWRITE_OLDEST) ? "ON" : "OFF"); + break; + case ROW_ADD_CHANNEL: startEditText(""); break; @@ -986,7 +1182,7 @@ public: } } - // Q: back — if radio changed, prompt to apply first + // Q: back — if radio changed, prompt to apply first if (c == 'q' || c == 'Q') { if (_radioChanged) { _editMode = EDIT_CONFIRM;