Unify PlatformIO and ESP-IDF around a shared UI/runtime shellRefactor/repo structure (#11)

* Unify PlatformIO and ESP-IDF around a shared UI/runtime shell
This commit is contained in:
vicliu
2026-03-12 01:17:46 +08:00
committed by GitHub
parent 76d83ef7bf
commit d320b6511d
989 changed files with 98068 additions and 15808 deletions
+239
View File
@@ -0,0 +1,239 @@
/**
* @file ui_status.cpp
* @brief Global UI status indicators (top bar icons + menu badges)
*/
#include "ui/ui_status.h"
#include "app/app_config.h"
#include "app/app_facade_access.h"
#include "chat/usecase/chat_service.h"
#include "platform/ui/gps_runtime.h"
#include "platform/ui/tracker_runtime.h"
#include "sys/clock.h"
#include "ui/screens/team/team_ui_store.h"
#include <cstdio>
extern "C"
{
extern const lv_image_dsc_t gps_topbar;
extern const lv_image_dsc_t message_topbar;
extern const lv_image_dsc_t route_topbar;
extern const lv_image_dsc_t team_topbar;
extern const lv_image_dsc_t tracker_topbar;
extern const lv_image_dsc_t ble_topbar;
}
namespace ui
{
namespace status
{
namespace
{
struct StatusSnapshot
{
bool route_active = false;
bool track_recording = false;
bool gps_enabled = false;
bool team_active = false;
bool ble_enabled = false;
int unread = 0;
};
struct TeamSnapshotCache
{
bool valid = false;
uint32_t last_refresh_ms = 0;
bool team_active = false;
int team_unread = 0;
};
lv_timer_t* s_status_timer = nullptr;
lv_obj_t* s_menu_status_row = nullptr;
lv_obj_t* s_menu_route_icon = nullptr;
lv_obj_t* s_menu_tracker_icon = nullptr;
lv_obj_t* s_menu_gps_icon = nullptr;
lv_obj_t* s_menu_team_icon = nullptr;
lv_obj_t* s_menu_msg_icon = nullptr;
lv_obj_t* s_menu_ble_icon = nullptr;
lv_obj_t* s_chat_badge = nullptr;
lv_obj_t* s_chat_badge_label = nullptr;
TeamSnapshotCache s_team_cache;
constexpr uint32_t kTeamSnapshotRefreshMs = 5000;
bool obj_valid(lv_obj_t* obj)
{
return obj && lv_obj_is_valid(obj);
}
void refresh_team_cache(bool force = false)
{
const uint32_t now = sys::millis_now();
if (!force && s_team_cache.valid && (now - s_team_cache.last_refresh_ms) < kTeamSnapshotRefreshMs)
{
return;
}
team::ui::TeamUiSnapshot snap;
if (team::ui::team_ui_get_store().load(snap))
{
s_team_cache.team_active = snap.in_team;
s_team_cache.team_unread = static_cast<int>(snap.team_chat_unread);
}
else
{
s_team_cache.team_active = false;
s_team_cache.team_unread = 0;
}
s_team_cache.valid = true;
s_team_cache.last_refresh_ms = now;
}
StatusSnapshot collect_status()
{
StatusSnapshot snap{};
const auto& cfg = app::configFacade().getConfig();
snap.route_active = cfg.route_enabled && (cfg.route_path[0] != '\0');
snap.track_recording = platform::ui::tracker::is_recording();
snap.gps_enabled = platform::ui::gps::is_enabled();
snap.ble_enabled = app::runtimeFacade().isBleEnabled();
refresh_team_cache();
snap.team_active = s_team_cache.team_active;
snap.unread = get_total_unread();
return snap;
}
void apply_icon(lv_obj_t* icon, const lv_image_dsc_t* src, bool visible)
{
if (!obj_valid(icon))
{
return;
}
if (src)
{
lv_image_set_src(icon, src);
}
if (visible)
{
lv_obj_clear_flag(icon, LV_OBJ_FLAG_HIDDEN);
}
else
{
lv_obj_add_flag(icon, LV_OBJ_FLAG_HIDDEN);
}
}
void apply_menu_icons(const StatusSnapshot& snap)
{
if (!obj_valid(s_menu_status_row))
{
return;
}
apply_icon(s_menu_route_icon, &route_topbar, snap.route_active);
apply_icon(s_menu_tracker_icon, &tracker_topbar, snap.track_recording);
apply_icon(s_menu_gps_icon, &gps_topbar, snap.gps_enabled);
apply_icon(s_menu_team_icon, &team_topbar, snap.team_active);
apply_icon(s_menu_msg_icon, &message_topbar, snap.unread > 0);
apply_icon(s_menu_ble_icon, &ble_topbar, snap.ble_enabled);
const bool any =
snap.route_active || snap.track_recording || snap.gps_enabled || snap.team_active ||
snap.ble_enabled || (snap.unread > 0);
if (any)
{
lv_obj_clear_flag(s_menu_status_row, LV_OBJ_FLAG_HIDDEN);
}
else
{
lv_obj_add_flag(s_menu_status_row, LV_OBJ_FLAG_HIDDEN);
}
}
void apply_menu_badge(const StatusSnapshot& snap)
{
if (!obj_valid(s_chat_badge) || !obj_valid(s_chat_badge_label))
{
return;
}
if (snap.unread <= 0)
{
lv_obj_add_flag(s_chat_badge, LV_OBJ_FLAG_HIDDEN);
return;
}
char buf[12];
snprintf(buf, sizeof(buf), "%d", snap.unread);
lv_label_set_text(s_chat_badge_label, buf);
lv_obj_clear_flag(s_chat_badge, LV_OBJ_FLAG_HIDDEN);
}
void status_timer_cb(lv_timer_t* /*timer*/)
{
StatusSnapshot snap = collect_status();
apply_menu_icons(snap);
apply_menu_badge(snap);
}
} // namespace
void init()
{
if (s_status_timer)
{
return;
}
s_status_timer = lv_timer_create(status_timer_cb, 1000, nullptr);
if (s_status_timer)
{
lv_timer_set_repeat_count(s_status_timer, -1);
}
status_timer_cb(nullptr);
}
void register_menu_status_row(lv_obj_t* row,
lv_obj_t* route_icon,
lv_obj_t* tracker_icon,
lv_obj_t* gps_icon,
lv_obj_t* team_icon,
lv_obj_t* msg_icon,
lv_obj_t* ble_icon)
{
s_menu_status_row = row;
s_menu_route_icon = route_icon;
s_menu_tracker_icon = tracker_icon;
s_menu_gps_icon = gps_icon;
s_menu_team_icon = team_icon;
s_menu_msg_icon = msg_icon;
s_menu_ble_icon = ble_icon;
status_timer_cb(nullptr);
}
void register_chat_badge(lv_obj_t* badge_bg, lv_obj_t* badge_label)
{
s_chat_badge = badge_bg;
s_chat_badge_label = badge_label;
status_timer_cb(nullptr);
}
void force_update()
{
refresh_team_cache(true);
status_timer_cb(nullptr);
}
int get_total_unread()
{
chat::ChatService& chat = app::messagingFacade().getChatService();
int unread = chat.getTotalUnread();
refresh_team_cache();
return unread + s_team_cache.team_unread;
}
} // namespace status
} // namespace ui