Files
pyxis/lib/tdeck_ui/UI/LXMF/StatusScreen.cpp
T
Torlando 28ea218a18 refactor(settings): move status readouts to Status screen, reorganize by use
Settings held live status (GPS fix, storage/RAM/identity) that belongs
on the Status screen, plus a per-second tick() doing SPI flash stat
reads and label churn mid-scroll — the main cause of laggy scrolling.

- GPS section (sats/location/altitude/HDOP/time) -> StatusScreen
- System Info (firmware build, storage, RAM) -> StatusScreen, with
  storage/RAM stat reads throttled to ~5s and stack-buffer snprintfs
  instead of Arduino String concatenation
- Settings gains a Status link row (trackball-reachable) that opens
  Route::STATUS; the per-second SettingsScreen tick/refresh is deleted
- Reordered sections by frequency of use: General (name/brightness/
  timeout/kb-light), Notifications, Network (now includes the
  TCP/Auto/BLE interface switches), Radio (LoRa + params), Delivery,
  Advanced, DANGER: Transport Mode (still final)
- Identity/LXMF hashes shown in Settings were truncated duplicates of
  the Status screen's full display; removed
- main.cpp publishes firmware build + GPS to the Status screen
- Contract test for the storage readout follows the code to StatusScreen
2026-09-04 04:56:07 +00:00

592 lines
22 KiB
C++

// Copyright (c) 2024 microReticulum contributors
// SPDX-License-Identifier: MIT
#include "StatusScreen.h"
#include "Theme.h"
#ifdef ARDUINO
#include <WiFi.h>
#include <LittleFS.h>
#include <TinyGPSPlus.h>
#include <microReticulum/Log.h>
#include "../LVGL/LVGLInit.h"
#include "../LVGL/LVGLLock.h"
using namespace RNS;
namespace UI {
namespace LXMF {
StatusScreen::StatusScreen(lv_obj_t* parent)
: _screen(nullptr), _header(nullptr), _content(nullptr), _btn_back(nullptr),
_btn_share(nullptr), _btn_radio_activity(nullptr), _label_uptime(nullptr),
_label_identity_value(nullptr), _label_lxmf_value(nullptr),
_label_wifi_status(nullptr), _label_wifi_ip(nullptr), _label_wifi_rssi(nullptr),
_label_rns_status(nullptr), _label_prop_node(nullptr), _label_ble_header(nullptr),
_label_gps_sats(nullptr), _label_gps_coords(nullptr), _label_gps_alt(nullptr),
_label_gps_hdop(nullptr), _label_gps_time(nullptr),
_label_firmware(nullptr), _label_storage(nullptr), _label_ram(nullptr),
_gps(nullptr), _last_heavy_ms(0U),
_rns_connected(false), _ble_peer_count(0) {
// Initialize BLE peer labels array
for (size_t i = 0; i < MAX_BLE_PEERS; i++) {
_label_ble_peers[i] = nullptr;
}
// Create screen object
if (parent) {
_screen = lv_obj_create(parent);
} else {
_screen = lv_obj_create(lv_scr_act());
}
lv_obj_set_size(_screen, LV_PCT(100), LV_PCT(100));
lv_obj_clear_flag(_screen, LV_OBJ_FLAG_SCROLLABLE);
lv_obj_set_style_bg_color(_screen, Theme::surface(), 0);
lv_obj_set_style_bg_opa(_screen, LV_OPA_COVER, 0);
lv_obj_set_style_pad_all(_screen, 0, 0);
lv_obj_set_style_border_width(_screen, 0, 0);
lv_obj_set_style_radius(_screen, 0, 0);
// Create UI components
create_header();
create_content();
// Hide by default
hide();
TRACE("StatusScreen created");
}
StatusScreen::~StatusScreen() {
LVGL_LOCK();
if (_screen) {
lv_obj_del(_screen);
}
}
void StatusScreen::create_header() {
_header = lv_obj_create(_screen);
lv_obj_set_size(_header, LV_PCT(100), 36);
lv_obj_align(_header, LV_ALIGN_TOP_MID, 0, 0);
lv_obj_set_style_bg_color(_header, Theme::surfaceHeader(), 0);
lv_obj_set_style_border_width(_header, 0, 0);
lv_obj_set_style_radius(_header, 0, 0);
lv_obj_set_style_pad_all(_header, 0, 0);
// Back button
_btn_back = lv_btn_create(_header);
lv_obj_set_size(_btn_back, 50, 28);
lv_obj_align(_btn_back, LV_ALIGN_LEFT_MID, 2, 0);
lv_obj_set_style_bg_color(_btn_back, Theme::btnSecondary(), 0);
lv_obj_set_style_bg_color(_btn_back, Theme::btnSecondaryPressed(), LV_STATE_PRESSED);
lv_obj_add_event_cb(_btn_back, on_back_clicked, LV_EVENT_CLICKED, this);
lv_obj_t* label_back = lv_label_create(_btn_back);
lv_label_set_text(label_back, LV_SYMBOL_LEFT);
lv_obj_center(label_back);
lv_obj_set_style_text_color(label_back, Theme::textSecondary(), 0);
// Title
lv_obj_t* title = lv_label_create(_header);
lv_label_set_text(title, "Status");
lv_obj_align(title, LV_ALIGN_LEFT_MID, 60, 0);
lv_obj_set_style_text_color(title, Theme::textPrimary(), 0);
lv_obj_set_style_text_font(title, &lv_font_montserrat_16, 0);
// Share button (QR code icon) on the right
_btn_share = lv_btn_create(_header);
lv_obj_set_size(_btn_share, 60, 28);
lv_obj_align(_btn_share, LV_ALIGN_RIGHT_MID, -2, 0);
lv_obj_set_style_bg_color(_btn_share, Theme::primary(), 0);
lv_obj_set_style_bg_color(_btn_share, Theme::primaryPressed(), LV_STATE_PRESSED);
lv_obj_add_event_cb(_btn_share, on_share_clicked, LV_EVENT_CLICKED, this);
lv_obj_t* label_share = lv_label_create(_btn_share);
lv_label_set_text(label_share, "Share");
lv_obj_center(label_share);
lv_obj_set_style_text_color(label_share, Theme::textPrimary(), 0);
}
void StatusScreen::create_content() {
_content = lv_obj_create(_screen);
lv_obj_set_size(_content, LV_PCT(100), 204); // 240 - 36 header
lv_obj_align(_content, LV_ALIGN_TOP_MID, 0, 36);
lv_obj_set_style_pad_all(_content, 8, 0);
lv_obj_set_style_bg_color(_content, Theme::surface(), 0);
lv_obj_set_style_border_width(_content, 0, 0);
lv_obj_set_style_radius(_content, 0, 0);
// Enable vertical scrolling with flex layout
lv_obj_set_flex_flow(_content, LV_FLEX_FLOW_COLUMN);
lv_obj_set_flex_align(_content, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_START);
lv_obj_set_scroll_dir(_content, LV_DIR_VER);
lv_obj_set_scrollbar_mode(_content, LV_SCROLLBAR_MODE_AUTO);
// Radio telemetry is a child of Status, not a persistent fifth nav tab.
_btn_radio_activity = lv_btn_create(_content);
lv_obj_set_size(_btn_radio_activity, LV_PCT(100), 34);
lv_obj_set_style_bg_color(_btn_radio_activity, Theme::surfaceInput(), 0);
lv_obj_set_style_bg_color(_btn_radio_activity, Theme::surfaceElevated(), LV_STATE_PRESSED);
lv_obj_add_event_cb(_btn_radio_activity, on_radio_activity_clicked, LV_EVENT_CLICKED, this);
lv_obj_t* radio_label = lv_label_create(_btn_radio_activity);
lv_label_set_text(radio_label, "Radio Activity " LV_SYMBOL_RIGHT);
lv_obj_align(radio_label, LV_ALIGN_LEFT_MID, 0, 0);
lv_obj_set_style_text_color(radio_label, Theme::textPrimary(), 0);
// Uptime section
_label_uptime = lv_label_create(_content);
lv_label_set_text(_label_uptime, "Uptime: 0s");
lv_obj_set_style_text_color(_label_uptime, Theme::textPrimary(), 0);
lv_obj_set_style_pad_bottom(_label_uptime, 8, 0);
// Identity section
lv_obj_t* label_identity = lv_label_create(_content);
lv_label_set_text(label_identity, "Identity:");
lv_obj_set_style_text_color(label_identity, Theme::textMuted(), 0);
_label_identity_value = lv_label_create(_content);
lv_label_set_text(_label_identity_value, "Loading...");
lv_obj_set_style_text_color(_label_identity_value, Theme::info(), 0);
lv_obj_set_style_text_font(_label_identity_value, &lv_font_montserrat_12, 0);
lv_obj_set_style_pad_left(_label_identity_value, 8, 0);
lv_obj_set_style_pad_bottom(_label_identity_value, 8, 0);
// LXMF Address section
lv_obj_t* label_lxmf = lv_label_create(_content);
lv_label_set_text(label_lxmf, "LXMF Address:");
lv_obj_set_style_text_color(label_lxmf, Theme::textMuted(), 0);
_label_lxmf_value = lv_label_create(_content);
lv_label_set_text(_label_lxmf_value, "Loading...");
lv_obj_set_style_text_color(_label_lxmf_value, Theme::success(), 0);
lv_obj_set_style_text_font(_label_lxmf_value, &lv_font_montserrat_12, 0);
lv_obj_set_style_pad_left(_label_lxmf_value, 8, 0);
lv_obj_set_style_pad_bottom(_label_lxmf_value, 8, 0);
// WiFi section
_label_wifi_status = lv_label_create(_content);
lv_label_set_text(_label_wifi_status, "WiFi: Checking...");
lv_obj_set_style_text_color(_label_wifi_status, Theme::textPrimary(), 0);
_label_wifi_ip = lv_label_create(_content);
lv_label_set_text(_label_wifi_ip, "");
lv_obj_set_style_text_color(_label_wifi_ip, Theme::textTertiary(), 0);
lv_obj_set_style_pad_left(_label_wifi_ip, 8, 0);
_label_wifi_rssi = lv_label_create(_content);
lv_label_set_text(_label_wifi_rssi, "");
lv_obj_set_style_text_color(_label_wifi_rssi, Theme::textTertiary(), 0);
lv_obj_set_style_pad_left(_label_wifi_rssi, 8, 0);
lv_obj_set_style_pad_bottom(_label_wifi_rssi, 8, 0);
// RNS section
_label_rns_status = lv_label_create(_content);
lv_label_set_text(_label_rns_status, "RNS: Checking...");
lv_obj_set_style_text_color(_label_rns_status, Theme::textPrimary(), 0);
lv_obj_set_width(_label_rns_status, lv_pct(100));
lv_label_set_long_mode(_label_rns_status, LV_LABEL_LONG_WRAP);
lv_obj_set_style_pad_bottom(_label_rns_status, 8, 0);
// Propagation node section
_label_prop_node = lv_label_create(_content);
lv_label_set_text(_label_prop_node, "Prop Node: None");
lv_obj_set_style_text_color(_label_prop_node, Theme::textPrimary(), 0);
lv_obj_set_width(_label_prop_node, lv_pct(100));
lv_label_set_long_mode(_label_prop_node, LV_LABEL_LONG_WRAP);
lv_obj_set_style_pad_bottom(_label_prop_node, 8, 0);
// GPS section (read-only live readouts, previously on the Settings screen)
_label_gps_sats = lv_label_create(_content);
lv_label_set_text(_label_gps_sats, "Satellites: --");
lv_obj_set_style_text_color(_label_gps_sats, Theme::textPrimary(), 0);
lv_obj_set_style_pad_bottom(_label_gps_sats, 2, 0);
_label_gps_coords = lv_label_create(_content);
lv_label_set_text(_label_gps_coords, "Location: No fix");
lv_obj_set_style_text_color(_label_gps_coords, Theme::textPrimary(), 0);
lv_obj_set_style_pad_bottom(_label_gps_coords, 2, 0);
_label_gps_alt = lv_label_create(_content);
lv_label_set_text(_label_gps_alt, "Altitude: --");
lv_obj_set_style_text_color(_label_gps_alt, Theme::textPrimary(), 0);
lv_obj_set_style_pad_bottom(_label_gps_alt, 2, 0);
_label_gps_hdop = lv_label_create(_content);
lv_label_set_text(_label_gps_hdop, "HDOP: --");
lv_obj_set_style_text_color(_label_gps_hdop, Theme::textPrimary(), 0);
lv_obj_set_style_pad_bottom(_label_gps_hdop, 2, 0);
_label_gps_time = lv_label_create(_content);
lv_label_set_text(_label_gps_time, "Time: --");
lv_obj_set_style_text_color(_label_gps_time, Theme::textPrimary(), 0);
lv_obj_set_style_pad_bottom(_label_gps_time, 8, 0);
// System section (firmware build, storage, RAM)
_label_firmware = lv_label_create(_content);
lv_label_set_text(_label_firmware, "Firmware: " FIRMWARE_VERSION);
lv_obj_set_style_text_color(_label_firmware, Theme::textPrimary(), 0);
_label_storage = lv_label_create(_content);
lv_label_set_text(_label_storage, "Storage: --");
lv_obj_set_style_text_color(_label_storage, Theme::textPrimary(), 0);
_label_ram = lv_label_create(_content);
lv_label_set_text(_label_ram, "RAM: --");
lv_obj_set_style_text_color(_label_ram, Theme::textPrimary(), 0);
lv_obj_set_style_pad_bottom(_label_ram, 8, 0);
// BLE section header
_label_ble_header = lv_label_create(_content);
lv_label_set_text(_label_ble_header, "BLE: No peers");
lv_obj_set_style_text_color(_label_ble_header, Theme::textPrimary(), 0);
// Pre-create BLE peer labels (hidden until needed)
for (size_t i = 0; i < MAX_BLE_PEERS; i++) {
_label_ble_peers[i] = lv_label_create(_content);
lv_label_set_text(_label_ble_peers[i], "");
lv_obj_set_style_text_color(_label_ble_peers[i], Theme::textTertiary(), 0);
lv_obj_set_style_text_font(_label_ble_peers[i], &lv_font_montserrat_12, 0);
lv_obj_set_style_pad_left(_label_ble_peers[i], 8, 0);
lv_obj_add_flag(_label_ble_peers[i], LV_OBJ_FLAG_HIDDEN);
}
}
void StatusScreen::set_identity_hash(const Bytes& hash) {
LVGL_LOCK();
_identity_hash = hash;
update_labels();
}
void StatusScreen::set_lxmf_address(const Bytes& hash) {
LVGL_LOCK();
_lxmf_address = hash;
update_labels();
}
void StatusScreen::set_rns_status(bool connected, const String& server_name) {
LVGL_LOCK();
_rns_connected = connected;
_rns_server = server_name;
update_labels();
}
void StatusScreen::set_ble_info(const BLEPeerInfo* peers, size_t count) {
LVGL_LOCK();
// Copy peer data to internal storage
_ble_peer_count = (count <= MAX_BLE_PEERS) ? count : MAX_BLE_PEERS;
for (size_t i = 0; i < _ble_peer_count; i++) {
memcpy(&_ble_peers[i], &peers[i], sizeof(BLEPeerInfo));
}
update_labels();
}
void StatusScreen::set_propagation_node(const String& display) {
LVGL_LOCK();
_prop_node_display = display;
update_labels();
}
void StatusScreen::set_gps(TinyGPSPlus* gps) {
_gps = gps;
}
void StatusScreen::set_firmware_version(const String& version) {
LVGL_LOCK();
_firmware_version = version;
update_system_labels();
}
void StatusScreen::refresh() {
LVGL_LOCK();
update_labels();
}
void StatusScreen::update_labels() {
// Update uptime
{
unsigned long ms = millis();
unsigned long total_secs = ms / 1000;
unsigned long days = total_secs / 86400;
unsigned long hours = (total_secs % 86400) / 3600;
unsigned long mins = (total_secs % 3600) / 60;
unsigned long secs = total_secs % 60;
char uptime_text[40];
if (days > 0) {
snprintf(uptime_text, sizeof(uptime_text), "Uptime: %lud %luh %lum %lus", days, hours, mins, secs);
} else if (hours > 0) {
snprintf(uptime_text, sizeof(uptime_text), "Uptime: %luh %lum %lus", hours, mins, secs);
} else if (mins > 0) {
snprintf(uptime_text, sizeof(uptime_text), "Uptime: %lum %lus", mins, secs);
} else {
snprintf(uptime_text, sizeof(uptime_text), "Uptime: %lus", secs);
}
lv_label_set_text(_label_uptime, uptime_text);
}
// Update identity - use stack buffer to avoid String fragmentation
if (_identity_hash.size() > 0) {
lv_label_set_text(_label_identity_value, _identity_hash.toHex().c_str());
}
// Update LXMF address - use stack buffer to avoid String fragmentation
if (_lxmf_address.size() > 0) {
lv_label_set_text(_label_lxmf_value, _lxmf_address.toHex().c_str());
}
// Update WiFi status - use snprintf to avoid String concatenation
if (WiFi.status() == WL_CONNECTED) {
lv_label_set_text(_label_wifi_status, "WiFi: Connected");
lv_obj_set_style_text_color(_label_wifi_status, Theme::success(), 0);
char ip_text[32];
snprintf(ip_text, sizeof(ip_text), "IP: %s", WiFi.localIP().toString().c_str());
lv_label_set_text(_label_wifi_ip, ip_text);
char rssi_text[24];
snprintf(rssi_text, sizeof(rssi_text), "RSSI: %d dBm", WiFi.RSSI());
lv_label_set_text(_label_wifi_rssi, rssi_text);
} else {
lv_label_set_text(_label_wifi_status, "WiFi: Disconnected");
lv_obj_set_style_text_color(_label_wifi_status, Theme::error(), 0);
lv_label_set_text(_label_wifi_ip, "");
lv_label_set_text(_label_wifi_rssi, "");
}
// Update RNS status - use snprintf to avoid String concatenation
if (_rns_connected) {
char rns_text[80];
if (_rns_server.length() > 0) {
snprintf(rns_text, sizeof(rns_text), "RNS: Connected (%s)", _rns_server.c_str());
} else {
snprintf(rns_text, sizeof(rns_text), "RNS: Connected");
}
lv_label_set_text(_label_rns_status, rns_text);
lv_obj_set_style_text_color(_label_rns_status, Theme::success(), 0);
} else {
lv_label_set_text(_label_rns_status, "RNS: Disconnected");
lv_obj_set_style_text_color(_label_rns_status, Theme::error(), 0);
}
// Update propagation node
if (_prop_node_display.length() > 0) {
char prop_text[80];
snprintf(prop_text, sizeof(prop_text), "Prop Node: %s", _prop_node_display.c_str());
lv_label_set_text(_label_prop_node, prop_text);
} else {
lv_label_set_text(_label_prop_node, "Prop Node: None");
}
// Update BLE peer info
if (_ble_peer_count > 0) {
char ble_header[32];
snprintf(ble_header, sizeof(ble_header), "BLE: %zu peer%s",
_ble_peer_count, _ble_peer_count == 1 ? "" : "s");
lv_label_set_text(_label_ble_header, ble_header);
lv_obj_set_style_text_color(_label_ble_header, Theme::success(), 0);
for (size_t i = 0; i < MAX_BLE_PEERS; i++) {
if (i < _ble_peer_count) {
// Format: "a1b2c3d4e5f6 -45 dBm\nAA:BB:CC:DD:EE:FF"
char peer_text[64];
if (_ble_peers[i].identity[0] != '\0') {
snprintf(peer_text, sizeof(peer_text), "%s %d dBm\n%s",
_ble_peers[i].identity, _ble_peers[i].rssi, _ble_peers[i].mac);
} else {
snprintf(peer_text, sizeof(peer_text), "(no identity) %d dBm\n%s",
_ble_peers[i].rssi, _ble_peers[i].mac);
}
lv_label_set_text(_label_ble_peers[i], peer_text);
lv_obj_clear_flag(_label_ble_peers[i], LV_OBJ_FLAG_HIDDEN);
} else {
lv_obj_add_flag(_label_ble_peers[i], LV_OBJ_FLAG_HIDDEN);
}
}
} else {
lv_label_set_text(_label_ble_header, "BLE: No peers");
lv_obj_set_style_text_color(_label_ble_header, Theme::textMuted(), 0);
// Hide all peer labels
for (size_t i = 0; i < MAX_BLE_PEERS; i++) {
lv_obj_add_flag(_label_ble_peers[i], LV_OBJ_FLAG_HIDDEN);
}
}
update_gps_labels();
update_system_labels();
}
void StatusScreen::update_gps_labels() {
// Current system clock (independent of the GPS object): verify the time-sync at
// a glance. A sane local date means a good fix synced; "not set" means unsynced.
{
time_t now = time(nullptr);
struct tm lt;
localtime_r(&now, &lt);
if (lt.tm_year + 1900 >= 2024) {
char tbuf[48];
strftime(tbuf, sizeof(tbuf), "Time: %Y-%m-%d %H:%M:%S", &lt);
lv_label_set_text(_label_gps_time, tbuf);
} else {
lv_label_set_text(_label_gps_time, "Time: not set (awaiting GPS)");
}
}
if (!_gps) {
lv_label_set_text(_label_gps_sats, "Satellites: N/A");
lv_label_set_text(_label_gps_coords, "Location: GPS not available");
lv_label_set_text(_label_gps_alt, "Altitude: --");
lv_label_set_text(_label_gps_hdop, "HDOP: --");
return;
}
char buf[64];
snprintf(buf, sizeof(buf), "Satellites: %d", (int)_gps->satellites.value());
lv_label_set_text(_label_gps_sats, buf);
if (_gps->location.isValid()) {
snprintf(buf, sizeof(buf), "Location: %.4f, %.4f",
_gps->location.lat(), _gps->location.lng());
lv_label_set_text(_label_gps_coords, buf);
lv_obj_set_style_text_color(_label_gps_coords, Theme::success(), 0);
} else {
lv_label_set_text(_label_gps_coords, "Location: No fix");
lv_obj_set_style_text_color(_label_gps_coords, Theme::error(), 0);
}
if (_gps->altitude.isValid()) {
snprintf(buf, sizeof(buf), "Altitude: %.1fm", _gps->altitude.meters());
lv_label_set_text(_label_gps_alt, buf);
} else {
lv_label_set_text(_label_gps_alt, "Altitude: --");
}
if (_gps->hdop.isValid()) {
double hdop = _gps->hdop.hdop();
const char* quality;
if (hdop < 1.0) quality = "Ideal";
else if (hdop < 2.0) quality = "Excellent";
else if (hdop < 5.0) quality = "Good";
else if (hdop < 10.0) quality = "Moderate";
else quality = "Poor";
snprintf(buf, sizeof(buf), "HDOP: %.1f (%s)", hdop, quality);
lv_label_set_text(_label_gps_hdop, buf);
} else {
lv_label_set_text(_label_gps_hdop, "HDOP: --");
}
}
void StatusScreen::update_system_labels() {
if (_label_firmware && _firmware_version.length() > 0) {
char fbuf[96];
snprintf(fbuf, sizeof(fbuf), "Firmware: %s", _firmware_version.c_str());
lv_label_set_text(_label_firmware, fbuf);
}
// Storage/RAM stat reads hit the SPI flash and the allocator; throttle them
// to ~5s so the per-second refresh stays cheap.
uint32_t now = millis();
if (now - _last_heavy_ms < 5000) return;
_last_heavy_ms = now;
char buf[48];
size_t total = LittleFS.totalBytes();
size_t used = LittleFS.usedBytes();
if (total == 0 || used > total) {
lv_label_set_text(_label_storage, "Storage: unavailable");
} else {
snprintf(buf, sizeof(buf), "Storage: %d KB free", (int)((total - used) / 1024));
lv_label_set_text(_label_storage, buf);
}
snprintf(buf, sizeof(buf), "RAM: %d KB free", (int)(ESP.getFreeHeap() / 1024));
lv_label_set_text(_label_ram, buf);
}
void StatusScreen::set_back_callback(BackCallback callback) {
_back_callback = callback;
}
void StatusScreen::set_share_callback(ShareCallback callback) {
_share_callback = callback;
}
void StatusScreen::set_radio_activity_callback(RadioActivityCallback callback) {
_radio_activity_callback = callback;
}
void StatusScreen::show() {
LVGL_LOCK();
update_labels(); // Update status when shown
lv_obj_clear_flag(_screen, LV_OBJ_FLAG_HIDDEN);
lv_obj_move_foreground(_screen);
// Add buttons to focus group for trackball navigation
lv_group_t* group = LVGL::LVGLInit::get_default_group();
if (group) {
if (_btn_back) {
lv_group_add_obj(group, _btn_back);
}
if (_btn_share) {
lv_group_add_obj(group, _btn_share);
}
if (_btn_radio_activity) {
lv_group_add_obj(group, _btn_radio_activity);
}
lv_group_focus_obj(_btn_back);
}
}
void StatusScreen::hide() {
LVGL_LOCK();
// Remove from focus group when hiding
lv_group_t* group = LVGL::LVGLInit::get_default_group();
if (group) {
if (_btn_back) {
lv_group_remove_obj(_btn_back);
}
if (_btn_share) {
lv_group_remove_obj(_btn_share);
}
if (_btn_radio_activity) {
lv_group_remove_obj(_btn_radio_activity);
}
}
lv_obj_add_flag(_screen, LV_OBJ_FLAG_HIDDEN);
}
lv_obj_t* StatusScreen::get_object() {
return _screen;
}
void StatusScreen::on_back_clicked(lv_event_t* event) {
StatusScreen* screen = (StatusScreen*)lv_event_get_user_data(event);
if (screen->_back_callback) {
screen->_back_callback();
}
}
void StatusScreen::on_share_clicked(lv_event_t* event) {
StatusScreen* screen = (StatusScreen*)lv_event_get_user_data(event);
if (screen->_share_callback) {
screen->_share_callback();
}
}
void StatusScreen::on_radio_activity_clicked(lv_event_t* event) {
StatusScreen* screen = static_cast<StatusScreen*>(lv_event_get_user_data(event));
if (screen && screen->_radio_activity_callback) {
screen->_radio_activity_callback();
}
}
} // namespace LXMF
} // namespace UI
#endif // ARDUINO