mirror of
https://github.com/vicliu624/trail-mate.git
synced 2026-07-09 13:31:47 +00:00
Refine node info UI and peer announcements
This commit is contained in:
@@ -5,6 +5,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
#include "chat/domain/contact_types.h"
|
||||
#include "lvgl.h"
|
||||
|
||||
@@ -13,62 +15,53 @@ namespace node_info
|
||||
namespace ui
|
||||
{
|
||||
|
||||
static constexpr std::size_t kNodeInfoTileCount = 9;
|
||||
static constexpr std::size_t kNodeInfoInfoLineCount = 8;
|
||||
|
||||
struct NodeInfoWidgets
|
||||
{
|
||||
lv_obj_t* root = nullptr;
|
||||
lv_obj_t* header = nullptr;
|
||||
lv_obj_t* content = nullptr;
|
||||
lv_obj_t* top_row = nullptr;
|
||||
lv_obj_t* info_card = nullptr;
|
||||
lv_obj_t* info_header = nullptr;
|
||||
lv_obj_t* info_footer = nullptr;
|
||||
lv_obj_t* location_card = nullptr;
|
||||
lv_obj_t* location_header = nullptr;
|
||||
lv_obj_t* location_map = nullptr;
|
||||
lv_obj_t* location_coords = nullptr;
|
||||
lv_obj_t* location_updated = nullptr;
|
||||
lv_obj_t* link_panel = nullptr;
|
||||
lv_obj_t* link_header = nullptr;
|
||||
lv_obj_t* link_row_1 = nullptr;
|
||||
lv_obj_t* link_row_2 = nullptr;
|
||||
|
||||
lv_obj_t* back_btn = nullptr;
|
||||
lv_obj_t* back_label = nullptr;
|
||||
lv_obj_t* title_label = nullptr;
|
||||
lv_obj_t* battery_label = nullptr;
|
||||
|
||||
lv_obj_t* avatar_bg = nullptr;
|
||||
lv_obj_t* avatar_label = nullptr;
|
||||
lv_obj_t* name_label = nullptr;
|
||||
lv_obj_t* desc_label = nullptr;
|
||||
lv_obj_t* map_stage = nullptr;
|
||||
lv_obj_t* tile_layer = nullptr;
|
||||
lv_obj_t* left_scrim = nullptr;
|
||||
lv_obj_t* right_scrim = nullptr;
|
||||
|
||||
lv_obj_t* id_label = nullptr;
|
||||
lv_obj_t* role_label = nullptr;
|
||||
lv_obj_t* lon_label = nullptr;
|
||||
lv_obj_t* lat_label = nullptr;
|
||||
lv_obj_t* no_position_label = nullptr;
|
||||
|
||||
lv_obj_t* location_title_label = nullptr;
|
||||
lv_obj_t* map_image = nullptr;
|
||||
lv_obj_t* map_label = nullptr;
|
||||
lv_obj_t* coords_latlon_label = nullptr;
|
||||
lv_obj_t* coords_acc_label = nullptr;
|
||||
lv_obj_t* coords_alt_label = nullptr;
|
||||
lv_obj_t* updated_label = nullptr;
|
||||
lv_obj_t* connection_line = nullptr;
|
||||
lv_obj_t* marker_node_ring = nullptr;
|
||||
lv_obj_t* marker_node_dot = nullptr;
|
||||
lv_obj_t* marker_self_ring = nullptr;
|
||||
lv_obj_t* marker_self_dot = nullptr;
|
||||
lv_obj_t* distance_label = nullptr;
|
||||
|
||||
lv_obj_t* link_title_label = nullptr;
|
||||
lv_obj_t* link_rssi_label = nullptr;
|
||||
lv_obj_t* link_snr_label = nullptr;
|
||||
lv_obj_t* link_ch_label = nullptr;
|
||||
lv_obj_t* link_sf_label = nullptr;
|
||||
lv_obj_t* link_bw_label = nullptr;
|
||||
lv_obj_t* link_hop_label = nullptr;
|
||||
lv_obj_t* link_last_heard_label = nullptr;
|
||||
lv_obj_t* zoom_in_btn = nullptr;
|
||||
lv_obj_t* zoom_out_btn = nullptr;
|
||||
lv_obj_t* zoom_in_label = nullptr;
|
||||
lv_obj_t* zoom_out_label = nullptr;
|
||||
|
||||
lv_obj_t* tile_images[kNodeInfoTileCount]{};
|
||||
lv_obj_t* info_labels[kNodeInfoInfoLineCount]{};
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Create an empty Node Info screen structure.
|
||||
* @brief Create the Node Info page widgets.
|
||||
*/
|
||||
NodeInfoWidgets create(lv_obj_t* parent);
|
||||
|
||||
/**
|
||||
* @brief Destroy the Node Info screen (if created).
|
||||
* @brief Destroy the Node Info page (if created).
|
||||
*/
|
||||
void destroy();
|
||||
|
||||
@@ -78,7 +71,7 @@ void destroy();
|
||||
const NodeInfoWidgets& widgets();
|
||||
|
||||
/**
|
||||
* @brief Update UI labels with NodeInfo data.
|
||||
* @brief Update UI widgets with NodeInfo data.
|
||||
*/
|
||||
void set_node_info(const chat::contacts::NodeInfo& node);
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* @file node_info_page_layout.h
|
||||
* @brief Node info page layout (structure only)
|
||||
* @brief Node info page layout helpers
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
@@ -17,18 +17,6 @@ namespace layout
|
||||
lv_obj_t* create_root(lv_obj_t* parent);
|
||||
lv_obj_t* create_header(lv_obj_t* root);
|
||||
lv_obj_t* create_content(lv_obj_t* root);
|
||||
lv_obj_t* create_top_row(lv_obj_t* content);
|
||||
lv_obj_t* create_info_card(lv_obj_t* top_row);
|
||||
lv_obj_t* create_info_header(lv_obj_t* info_card);
|
||||
lv_obj_t* create_info_footer(lv_obj_t* info_card);
|
||||
lv_obj_t* create_location_card(lv_obj_t* top_row);
|
||||
lv_obj_t* create_location_header(lv_obj_t* location_card);
|
||||
lv_obj_t* create_location_map(lv_obj_t* location_card);
|
||||
lv_obj_t* create_location_coords(lv_obj_t* location_card);
|
||||
lv_obj_t* create_location_updated(lv_obj_t* location_card);
|
||||
lv_obj_t* create_link_panel(lv_obj_t* content);
|
||||
lv_obj_t* create_link_header(lv_obj_t* link_panel);
|
||||
lv_obj_t* create_link_row(lv_obj_t* link_panel);
|
||||
|
||||
} // namespace layout
|
||||
} // namespace ui
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,66 +1,10 @@
|
||||
/**
|
||||
* UI Wireframe / Layout Tree (Node Info - matches provided mock)
|
||||
* --------------------------------------------------------------------
|
||||
*
|
||||
* Root (COLUMN)
|
||||
* +------------------------------------------------------------------+
|
||||
* | Header (TopBar) [< Back] NODE INFO [Battery] |
|
||||
* +------------------------------------------------------------------+
|
||||
* | TopRow (ROW, grow) |
|
||||
* | +-------------------------+ +---------------------------------+ |
|
||||
* | | Info Card (left) | | Location Card (right) | |
|
||||
* | | +---------------------+ | | +---------------------------+ | |
|
||||
* | | | Avatar (S) | | | | Title: Location | | |
|
||||
* | | | Name: ALFA-3 | | | +---------------------------+ | |
|
||||
* | | | Desc: Relay Node | | | | Map Placeholder | | |
|
||||
* | | +---------------------+ | | | [Map image] | | |
|
||||
* | | | ID: !a1b2c3 | | | +---------------------------+ | |
|
||||
* | | | Role: Router | | | | Lat,Lon +/-Acc Alt | | |
|
||||
* | | +---------------------+ | | +---------------------------+ | |
|
||||
* | | | | | Updated: 2m ago | | |
|
||||
* | +-------------------------+ | +---------------------------+ | |
|
||||
* | +---------------------------------+ |
|
||||
* | |
|
||||
* | Link Panel (full width) |
|
||||
* | +------------------------------------------------------------+ |
|
||||
* | | Title: Link | |
|
||||
* | +------------------------------------------------------------+ |
|
||||
* | | RSSI: -112 SNR: 7.5 Ch: 478.875 SF: 7 BW: 125k | |
|
||||
* | +------------------------------------------------------------+ |
|
||||
* | | Hop: 2 Last heard: 18s | |
|
||||
* | +------------------------------------------------------------+ |
|
||||
* +------------------------------------------------------------------+
|
||||
*
|
||||
* Tree view:
|
||||
* Root(COL)
|
||||
* - Header
|
||||
* - Content(COL)
|
||||
* - TopRow(ROW, grow=1)
|
||||
* - InfoCard(COL)
|
||||
* - InfoHeader(ROW)
|
||||
* - InfoFooter(ROW)
|
||||
* - LocationCard(COL, grow=1)
|
||||
* - LocationHeader
|
||||
* - LocationMap(grow=1)
|
||||
* - LocationCoords
|
||||
* - LocationUpdated
|
||||
* - LinkPanel(COL)
|
||||
* - LinkHeader
|
||||
* - LinkRow1
|
||||
* - LinkRow2
|
||||
*
|
||||
* Preconditions:
|
||||
* - Root uses LV_FLEX_FLOW_COLUMN.
|
||||
* - TopRow uses LV_FLEX_FLOW_ROW.
|
||||
* - LinkPanel uses LV_FLEX_FLOW_COLUMN.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file node_info_page_layout.cpp
|
||||
* @brief Node info page layout
|
||||
* @brief Node info page layout helpers
|
||||
*/
|
||||
|
||||
#include "ui/screens/node_info/node_info_page_layout.h"
|
||||
|
||||
#include "ui/page/page_profile.h"
|
||||
#include "ui/widgets/top_bar.h"
|
||||
|
||||
@@ -73,8 +17,36 @@ namespace layout
|
||||
|
||||
namespace
|
||||
{
|
||||
constexpr lv_coord_t kScreenWidth = 480;
|
||||
constexpr lv_coord_t kBaseContentHeight = 192;
|
||||
|
||||
lv_coord_t resolve_parent_width(lv_obj_t* parent)
|
||||
{
|
||||
if (parent)
|
||||
{
|
||||
const lv_coord_t width = lv_obj_get_width(parent);
|
||||
if (width > 0)
|
||||
{
|
||||
return width;
|
||||
}
|
||||
}
|
||||
|
||||
const lv_coord_t width = lv_display_get_physical_horizontal_resolution(nullptr);
|
||||
return width > 0 ? width : 320;
|
||||
}
|
||||
|
||||
lv_coord_t resolve_parent_height(lv_obj_t* parent)
|
||||
{
|
||||
if (parent)
|
||||
{
|
||||
const lv_coord_t height = lv_obj_get_height(parent);
|
||||
if (height > 0)
|
||||
{
|
||||
return height;
|
||||
}
|
||||
}
|
||||
|
||||
const lv_coord_t height = lv_display_get_physical_vertical_resolution(nullptr);
|
||||
return height > 0 ? height : 240;
|
||||
}
|
||||
|
||||
lv_coord_t top_bar_height()
|
||||
{
|
||||
@@ -83,196 +55,45 @@ lv_coord_t top_bar_height()
|
||||
: static_cast<lv_coord_t>(::ui::widgets::kTopBarHeight);
|
||||
}
|
||||
|
||||
lv_coord_t screen_height()
|
||||
{
|
||||
return top_bar_height() + kBaseContentHeight;
|
||||
}
|
||||
constexpr int kContentPadX = 8;
|
||||
constexpr int kContentPadY = 4;
|
||||
constexpr int kCardGapX = 8;
|
||||
constexpr int kRowGap = 8;
|
||||
|
||||
constexpr int kTopRowX = kContentPadX;
|
||||
constexpr int kTopRowY = kContentPadY;
|
||||
constexpr int kTopRowWidth = kScreenWidth - (kContentPadX * 2);
|
||||
constexpr int kTopRowHeight = 118;
|
||||
|
||||
constexpr int kInfoCardWidth = 190;
|
||||
constexpr int kInfoCardHeight = 118;
|
||||
constexpr int kLocationCardWidth = 266;
|
||||
constexpr int kLocationCardHeight = 118;
|
||||
|
||||
constexpr int kLinkPanelX = kContentPadX;
|
||||
constexpr int kLinkPanelY = kTopRowY + kTopRowHeight + kRowGap;
|
||||
constexpr int kLinkPanelWidth = kTopRowWidth;
|
||||
constexpr int kLinkPanelHeight = 60;
|
||||
|
||||
constexpr int kLocationHeaderHeight = 22;
|
||||
constexpr int kLocationMapHeight = 52;
|
||||
constexpr int kLocationCoordsHeight = 18;
|
||||
constexpr int kLocationUpdatedHeight = 18;
|
||||
|
||||
constexpr int kInfoHeaderHeight = 70;
|
||||
constexpr int kInfoFooterHeight = 36;
|
||||
|
||||
constexpr int kLinkHeaderHeight = 20;
|
||||
constexpr int kLinkRowHeight = 16;
|
||||
|
||||
void make_non_scrollable(lv_obj_t* obj)
|
||||
void make_plain(lv_obj_t* obj)
|
||||
{
|
||||
lv_obj_clear_flag(obj, LV_OBJ_FLAG_SCROLLABLE);
|
||||
lv_obj_set_scrollbar_mode(obj, LV_SCROLLBAR_MODE_OFF);
|
||||
lv_obj_set_style_pad_all(obj, 0, 0);
|
||||
lv_obj_set_style_border_width(obj, 0, 0);
|
||||
lv_obj_set_style_radius(obj, 0, 0);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
lv_obj_t* create_root(lv_obj_t* parent)
|
||||
{
|
||||
lv_obj_t* root = lv_obj_create(parent);
|
||||
lv_obj_set_size(root, kScreenWidth, screen_height());
|
||||
lv_obj_set_size(root, resolve_parent_width(parent), resolve_parent_height(parent));
|
||||
lv_obj_set_pos(root, 0, 0);
|
||||
lv_obj_set_style_pad_all(root, 0, 0);
|
||||
make_non_scrollable(root);
|
||||
make_plain(root);
|
||||
return root;
|
||||
}
|
||||
|
||||
lv_obj_t* create_header(lv_obj_t* root)
|
||||
{
|
||||
lv_obj_t* header = lv_obj_create(root);
|
||||
lv_obj_set_size(header, kScreenWidth, top_bar_height());
|
||||
lv_obj_set_size(header, lv_obj_get_width(root), top_bar_height());
|
||||
lv_obj_set_pos(header, 0, 0);
|
||||
make_non_scrollable(header);
|
||||
make_plain(header);
|
||||
return header;
|
||||
}
|
||||
|
||||
lv_obj_t* create_content(lv_obj_t* root)
|
||||
{
|
||||
const lv_coord_t header_h = top_bar_height();
|
||||
lv_obj_t* content = lv_obj_create(root);
|
||||
lv_obj_set_size(content, kScreenWidth, kBaseContentHeight);
|
||||
lv_obj_set_pos(content, 0, top_bar_height());
|
||||
lv_obj_set_style_pad_all(content, 0, 0);
|
||||
make_non_scrollable(content);
|
||||
lv_obj_set_size(content, lv_obj_get_width(root), lv_obj_get_height(root) - header_h);
|
||||
lv_obj_set_pos(content, 0, header_h);
|
||||
make_plain(content);
|
||||
return content;
|
||||
}
|
||||
|
||||
lv_obj_t* create_top_row(lv_obj_t* content)
|
||||
{
|
||||
lv_obj_t* row = lv_obj_create(content);
|
||||
lv_obj_set_size(row, kTopRowWidth, kTopRowHeight);
|
||||
lv_obj_set_pos(row, kTopRowX, kTopRowY);
|
||||
lv_obj_set_style_pad_all(row, 0, 0);
|
||||
make_non_scrollable(row);
|
||||
return row;
|
||||
}
|
||||
|
||||
lv_obj_t* create_info_card(lv_obj_t* top_row)
|
||||
{
|
||||
lv_obj_t* card = lv_obj_create(top_row);
|
||||
lv_obj_set_size(card, kInfoCardWidth, kInfoCardHeight);
|
||||
lv_obj_set_pos(card, 0, 0);
|
||||
lv_obj_set_style_pad_all(card, 0, 0);
|
||||
make_non_scrollable(card);
|
||||
return card;
|
||||
}
|
||||
|
||||
lv_obj_t* create_info_header(lv_obj_t* info_card)
|
||||
{
|
||||
lv_obj_t* header = lv_obj_create(info_card);
|
||||
lv_obj_set_size(header, kInfoCardWidth, kInfoHeaderHeight);
|
||||
lv_obj_set_pos(header, 0, 0);
|
||||
lv_obj_set_style_pad_all(header, 0, 0);
|
||||
make_non_scrollable(header);
|
||||
return header;
|
||||
}
|
||||
|
||||
lv_obj_t* create_info_footer(lv_obj_t* info_card)
|
||||
{
|
||||
lv_obj_t* footer = lv_obj_create(info_card);
|
||||
lv_obj_set_size(footer, 170, kInfoFooterHeight);
|
||||
lv_obj_set_pos(footer, 10, 70);
|
||||
lv_obj_set_style_pad_all(footer, 0, 0);
|
||||
make_non_scrollable(footer);
|
||||
return footer;
|
||||
}
|
||||
|
||||
lv_obj_t* create_location_card(lv_obj_t* top_row)
|
||||
{
|
||||
lv_obj_t* card = lv_obj_create(top_row);
|
||||
lv_obj_set_size(card, kLocationCardWidth, kLocationCardHeight);
|
||||
lv_obj_set_pos(card, kInfoCardWidth + kCardGapX, 0);
|
||||
lv_obj_set_style_pad_all(card, 0, 0);
|
||||
make_non_scrollable(card);
|
||||
return card;
|
||||
}
|
||||
|
||||
lv_obj_t* create_location_header(lv_obj_t* location_card)
|
||||
{
|
||||
lv_obj_t* header = lv_obj_create(location_card);
|
||||
lv_obj_set_size(header, kLocationCardWidth, kLocationHeaderHeight);
|
||||
lv_obj_set_pos(header, 0, 0);
|
||||
lv_obj_set_style_pad_all(header, 0, 0);
|
||||
make_non_scrollable(header);
|
||||
return header;
|
||||
}
|
||||
|
||||
lv_obj_t* create_location_map(lv_obj_t* location_card)
|
||||
{
|
||||
lv_obj_t* map = lv_obj_create(location_card);
|
||||
lv_obj_set_size(map, 246, kLocationMapHeight);
|
||||
lv_obj_set_pos(map, 10, 30);
|
||||
lv_obj_set_style_pad_all(map, 0, 0);
|
||||
make_non_scrollable(map);
|
||||
return map;
|
||||
}
|
||||
|
||||
lv_obj_t* create_location_coords(lv_obj_t* location_card)
|
||||
{
|
||||
lv_obj_t* coords = lv_obj_create(location_card);
|
||||
lv_obj_set_size(coords, 246, kLocationCoordsHeight);
|
||||
lv_obj_set_pos(coords, 10, 86);
|
||||
lv_obj_set_style_pad_all(coords, 0, 0);
|
||||
make_non_scrollable(coords);
|
||||
return coords;
|
||||
}
|
||||
|
||||
lv_obj_t* create_location_updated(lv_obj_t* location_card)
|
||||
{
|
||||
lv_obj_t* updated = lv_obj_create(location_card);
|
||||
lv_obj_set_size(updated, 246, kLocationUpdatedHeight);
|
||||
lv_obj_set_pos(updated, 10, 106);
|
||||
lv_obj_set_style_pad_all(updated, 0, 0);
|
||||
make_non_scrollable(updated);
|
||||
return updated;
|
||||
}
|
||||
|
||||
lv_obj_t* create_link_panel(lv_obj_t* content)
|
||||
{
|
||||
lv_obj_t* panel = lv_obj_create(content);
|
||||
lv_obj_set_size(panel, kLinkPanelWidth, kLinkPanelHeight);
|
||||
lv_obj_set_pos(panel, kLinkPanelX, kLinkPanelY);
|
||||
lv_obj_set_style_pad_all(panel, 0, 0);
|
||||
make_non_scrollable(panel);
|
||||
return panel;
|
||||
}
|
||||
|
||||
lv_obj_t* create_link_header(lv_obj_t* link_panel)
|
||||
{
|
||||
lv_obj_t* header = lv_obj_create(link_panel);
|
||||
lv_obj_set_size(header, kLinkPanelWidth, kLinkHeaderHeight);
|
||||
lv_obj_set_pos(header, 0, 0);
|
||||
lv_obj_set_style_pad_all(header, 0, 0);
|
||||
make_non_scrollable(header);
|
||||
return header;
|
||||
}
|
||||
|
||||
lv_obj_t* create_link_row(lv_obj_t* link_panel)
|
||||
{
|
||||
lv_obj_t* row = lv_obj_create(link_panel);
|
||||
lv_obj_set_size(row, kLinkPanelWidth, kLinkRowHeight);
|
||||
lv_obj_set_style_pad_all(row, 0, 0);
|
||||
make_non_scrollable(row);
|
||||
return row;
|
||||
}
|
||||
|
||||
} // namespace layout
|
||||
} // namespace ui
|
||||
} // namespace node_info
|
||||
|
||||
@@ -216,16 +216,16 @@ void enter(const shell::Host* host, lv_obj_t* parent)
|
||||
lv_obj_center(stack);
|
||||
|
||||
lv_obj_t* title = lv_label_create(stack);
|
||||
::ui::i18n::set_label_text(title, page_subtitle());
|
||||
lv_obj_set_style_text_font(title, &lv_font_montserrat_18, 0);
|
||||
::ui::i18n::set_label_text(title, page_subtitle());
|
||||
|
||||
s_status_label = lv_label_create(stack);
|
||||
::ui::i18n::set_label_text(s_status_label, "Waiting for host...");
|
||||
lv_obj_set_style_text_font(s_status_label, &lv_font_montserrat_18, 0);
|
||||
::ui::i18n::set_label_text(s_status_label, "Waiting for host...");
|
||||
|
||||
s_count_label = lv_label_create(stack);
|
||||
::ui::i18n::set_label_text(s_count_label, "RX: 0 TX: 0");
|
||||
lv_obj_set_style_text_font(s_count_label, &lv_font_montserrat_16, 0);
|
||||
::ui::i18n::set_label_text(s_count_label, "RX: 0 TX: 0");
|
||||
|
||||
if (!s_timer)
|
||||
{
|
||||
|
||||
@@ -272,26 +272,26 @@ void enter(const shell::Host* host, lv_obj_t* parent)
|
||||
if (!platform::ui::device::card_ready())
|
||||
{
|
||||
lv_obj_t* error_label = lv_label_create(s_content);
|
||||
::ui::i18n::set_label_text(error_label, "SD Card Not Found\nPlease insert SD card");
|
||||
lv_obj_center(error_label);
|
||||
lv_obj_set_style_text_font(error_label, &lv_font_montserrat_18, LV_PART_MAIN);
|
||||
lv_obj_set_style_text_color(error_label, lv_color_hex(0xCC0000), LV_PART_MAIN);
|
||||
::ui::i18n::set_label_text(error_label, "SD Card Not Found\nPlease insert SD card");
|
||||
lv_obj_center(error_label);
|
||||
return;
|
||||
}
|
||||
|
||||
s_status_label = lv_label_create(s_content);
|
||||
::ui::i18n::set_label_text(s_status_label, "Initializing...");
|
||||
lv_obj_center(s_status_label);
|
||||
lv_obj_set_style_text_font(s_status_label, &lv_font_montserrat_18, LV_PART_MAIN);
|
||||
lv_obj_set_style_text_color(s_status_label, lv_color_hex(0x3A2A1A), LV_PART_MAIN);
|
||||
lv_obj_set_style_text_align(s_status_label, LV_TEXT_ALIGN_CENTER, LV_PART_MAIN);
|
||||
::ui::i18n::set_label_text(s_status_label, "Initializing...");
|
||||
lv_obj_center(s_status_label);
|
||||
|
||||
lv_obj_t* info_label = lv_label_create(s_content);
|
||||
::ui::i18n::set_label_text(info_label, "Press Back to exit USB mode");
|
||||
lv_obj_align(info_label, LV_ALIGN_BOTTOM_MID, 0, -20);
|
||||
lv_obj_set_style_text_font(info_label, &lv_font_montserrat_14, LV_PART_MAIN);
|
||||
lv_obj_set_style_text_color(info_label, lv_color_hex(0x6A5646), LV_PART_MAIN);
|
||||
lv_obj_set_style_text_opa(info_label, LV_OPA_80, LV_PART_MAIN);
|
||||
::ui::i18n::set_label_text(info_label, "Press Back to exit USB mode");
|
||||
lv_obj_align(info_label, LV_ALIGN_BOTTOM_MID, 0, -20);
|
||||
|
||||
update_status_label();
|
||||
|
||||
|
||||
+5
@@ -197,6 +197,7 @@ class MtAdapter : public chat::IMeshAdapter
|
||||
static constexpr uint8_t MAX_RETRIES = 3;
|
||||
static constexpr uint32_t NODEINFO_INTERVAL_MS = 3 * 60 * 60 * 1000;
|
||||
static constexpr uint32_t NODEINFO_REPLY_SUPPRESS_MS = 12 * 60 * 60 * 1000;
|
||||
static constexpr uint32_t NODEINFO_REANNOUNCE_SUPPRESS_MS = 60 * 1000;
|
||||
static constexpr uint32_t POSITION_REPLY_SUPPRESS_MS = 3 * 60 * 1000;
|
||||
static constexpr uint32_t PKI_BACKOFF_MS = 5 * 60 * 1000;
|
||||
static constexpr size_t MAX_APP_QUEUE = 10;
|
||||
@@ -230,6 +231,10 @@ class MtAdapter : public chat::IMeshAdapter
|
||||
bool want_ack_flag,
|
||||
bool want_response);
|
||||
void maybeBroadcastNodeInfo(uint32_t now_ms);
|
||||
void maybeBroadcastNodeInfoAfterPeerAnnouncement(uint32_t from_node,
|
||||
uint32_t now_ms,
|
||||
ChannelId channel,
|
||||
bool from_mqtt);
|
||||
void configureRadio();
|
||||
void initNodeIdentity();
|
||||
void updateChannelKeys();
|
||||
|
||||
@@ -1,21 +1,85 @@
|
||||
#include "platform/esp/arduino_common/app_runtime_support.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
|
||||
#include "app/app_config.h"
|
||||
#include "app/app_facade_access.h"
|
||||
#include "app/app_facades.h"
|
||||
#include "ble/ble_manager.h"
|
||||
#include "board/BoardBase.h"
|
||||
#include "chat/usecase/chat_service.h"
|
||||
#include "chat/usecase/contact_service.h"
|
||||
#include "platform/esp/arduino_common/app_tasks.h"
|
||||
#include "platform/esp/arduino_common/device_identity.h"
|
||||
#include "platform/esp/arduino_common/hostlink/hostlink_service.h"
|
||||
#include "platform/ui/settings_store.h"
|
||||
#include "sys/event_bus.h"
|
||||
#include "team/usecase/team_pairing_service.h"
|
||||
#include "team/usecase/team_service.h"
|
||||
#include "team/usecase/team_track_sampler.h"
|
||||
#include "ui/localization.h"
|
||||
#include "ui/widgets/system_notification.h"
|
||||
|
||||
namespace platform::esp::arduino_common
|
||||
{
|
||||
namespace
|
||||
{
|
||||
|
||||
void triggerNodeInfoFeedback(app::IAppFacade& app_context)
|
||||
{
|
||||
BoardBase* board = app_context.getBoard();
|
||||
if (!board)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (platform::ui::settings_store::get_bool("settings", "vibration_enabled", true))
|
||||
{
|
||||
board->vibrator();
|
||||
}
|
||||
|
||||
board->playMessageTone();
|
||||
}
|
||||
|
||||
std::string resolveNodeInfoName(app::IAppFacade& app_context, const sys::NodeInfoUpdateEvent& node_event)
|
||||
{
|
||||
std::string name = app_context.getContactService().getContactName(node_event.node_id);
|
||||
if (!name.empty())
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
if (node_event.short_name[0] != '\0')
|
||||
{
|
||||
return std::string(node_event.short_name);
|
||||
}
|
||||
|
||||
if (node_event.long_name[0] != '\0')
|
||||
{
|
||||
return std::string(node_event.long_name);
|
||||
}
|
||||
|
||||
char fallback[16];
|
||||
std::snprintf(fallback, sizeof(fallback), "%08lX", static_cast<unsigned long>(node_event.node_id));
|
||||
return fallback;
|
||||
}
|
||||
|
||||
void notifyNodeInfoUpdate(app::IAppFacade& app_context, const sys::NodeInfoUpdateEvent& node_event)
|
||||
{
|
||||
if (node_event.node_id == 0 || node_event.node_id == app_context.getSelfNodeId() || node_event.is_ignored)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
triggerNodeInfoFeedback(app_context);
|
||||
|
||||
const std::string message =
|
||||
::ui::i18n::format("Node info: %s", resolveNodeInfoName(app_context, node_event).c_str());
|
||||
::ui::SystemNotification::show(message.c_str(), 3000);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
BackgroundTaskStartResult startBackgroundTasks(LoraBoard* board, chat::IMeshAdapter* adapter)
|
||||
{
|
||||
@@ -134,6 +198,7 @@ bool dispatchEvent(app::IAppFacade& app_context, sys::Event* event)
|
||||
update.device_metrics = node_event->device_metrics;
|
||||
}
|
||||
app_context.getContactService().applyNodeUpdate(node_event->node_id, update);
|
||||
notifyNodeInfoUpdate(app_context, *node_event);
|
||||
delete event;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -2142,6 +2142,14 @@ void MtAdapter::processReceivedPacket(const uint8_t* data, size_t size)
|
||||
{
|
||||
node_last_channel_[header.from] = channel_id;
|
||||
}
|
||||
if (nodeinfo_decoded)
|
||||
{
|
||||
maybeBroadcastNodeInfoAfterPeerAnnouncement(
|
||||
header.from,
|
||||
millis(),
|
||||
channel_id,
|
||||
(header.flags & chat::meshtastic::PACKET_FLAGS_VIA_MQTT_MASK) != 0);
|
||||
}
|
||||
if (want_ack_flag && to_us)
|
||||
{
|
||||
if (sendRoutingAck(header.from, header.id, header.channel, psk, psk_len))
|
||||
@@ -2702,6 +2710,10 @@ bool MtAdapter::sendNodeInfoTo(uint32_t dest, bool want_response, ChannelId chan
|
||||
}
|
||||
|
||||
bool ok = transmitWirePacket(wire_buffer, wire_size);
|
||||
if (ok && dest == 0xFFFFFFFF)
|
||||
{
|
||||
last_nodeinfo_ms_ = millis();
|
||||
}
|
||||
LORA_LOG("[LORA] TX nodeinfo id=%08lX len=%u ok=%d\n",
|
||||
(unsigned long)msg_id,
|
||||
(unsigned)wire_size,
|
||||
@@ -2844,6 +2856,39 @@ void MtAdapter::maybeBroadcastNodeInfo(uint32_t now_ms)
|
||||
}
|
||||
}
|
||||
|
||||
void MtAdapter::maybeBroadcastNodeInfoAfterPeerAnnouncement(uint32_t from_node,
|
||||
uint32_t now_ms,
|
||||
ChannelId channel,
|
||||
bool from_mqtt)
|
||||
{
|
||||
if (!ready_ || from_mqtt || from_node == 0 || from_node == node_id_)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (last_nodeinfo_ms_ != 0 &&
|
||||
(now_ms - last_nodeinfo_ms_) < NODEINFO_REANNOUNCE_SUPPRESS_MS)
|
||||
{
|
||||
LORA_LOG("[LORA] TX nodeinfo announce suppressed from=%08lX age=%lu\n",
|
||||
(unsigned long)from_node,
|
||||
(unsigned long)(now_ms - last_nodeinfo_ms_));
|
||||
return;
|
||||
}
|
||||
|
||||
if (sendNodeInfoTo(0xFFFFFFFF, false, channel))
|
||||
{
|
||||
LORA_LOG("[LORA] TX nodeinfo announce after peer from=%08lX ch=%u\n",
|
||||
(unsigned long)from_node,
|
||||
static_cast<unsigned>(channel));
|
||||
}
|
||||
else
|
||||
{
|
||||
LORA_LOG("[LORA] TX nodeinfo announce fail after peer from=%08lX ch=%u\n",
|
||||
(unsigned long)from_node,
|
||||
static_cast<unsigned>(channel));
|
||||
}
|
||||
}
|
||||
|
||||
void MtAdapter::configureRadio()
|
||||
{
|
||||
if (!board_.isRadioOnline())
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include <Preferences.h>
|
||||
#include <SD.h>
|
||||
#include <string>
|
||||
|
||||
namespace chat
|
||||
{
|
||||
@@ -15,6 +16,11 @@ namespace infra
|
||||
namespace
|
||||
{
|
||||
|
||||
std::string makeTempSdPath(const char* path)
|
||||
{
|
||||
return std::string(path ? path : "") + ".tmp";
|
||||
}
|
||||
|
||||
bool isValidPrefKey(const char* key)
|
||||
{
|
||||
return key && key[0] != '\0';
|
||||
@@ -117,12 +123,18 @@ bool saveRawBlobToSd(const char* path, const uint8_t* data, size_t len)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (SD.exists(path))
|
||||
const std::string temp_path = makeTempSdPath(path);
|
||||
if (temp_path.empty())
|
||||
{
|
||||
SD.remove(path);
|
||||
return false;
|
||||
}
|
||||
|
||||
File file = SD.open(path, FILE_WRITE);
|
||||
if (SD.exists(temp_path.c_str()))
|
||||
{
|
||||
SD.remove(temp_path.c_str());
|
||||
}
|
||||
|
||||
File file = SD.open(temp_path.c_str(), FILE_WRITE);
|
||||
if (!file)
|
||||
{
|
||||
return false;
|
||||
@@ -134,7 +146,24 @@ bool saveRawBlobToSd(const char* path, const uint8_t* data, size_t len)
|
||||
ok = (file.write(data, len) == len);
|
||||
}
|
||||
file.close();
|
||||
return ok;
|
||||
if (!ok)
|
||||
{
|
||||
SD.remove(temp_path.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (SD.exists(path))
|
||||
{
|
||||
SD.remove(path);
|
||||
}
|
||||
|
||||
if (!SD.rename(temp_path.c_str(), path))
|
||||
{
|
||||
SD.remove(temp_path.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool loadRawBlobFromPreferences(const char* ns, const char* key, std::vector<uint8_t>& out)
|
||||
|
||||
Reference in New Issue
Block a user