mirror of
https://github.com/vicliu624/trail-mate.git
synced 2026-08-28 13:34:07 +00:00
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:
@@ -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
|
||||
Reference in New Issue
Block a user