Files
stc32g128k/App/app_manager.c
edisondeng 56b92a8ede refactor(app): App-per-screen navigation, remove menu_level entirely
Splits the ad-hoc menu_level state machine into standalone Apps with a
uniform parent/child exit convention (long-press CONFIRM -> parent):
clock_app renamed to home_app (boot-default), new set_clock_app
extracted from home_app's time-edit mode, new ipc_bind_app extracted
from menu_app's old level-2 screen, and the sensor-type selection step
merged into pair_app as its new initial state. menu_app now only holds
the 4-item top-level list (language toggle inline). Also makes both
menu_app and pair_app's up/down navigation cyclic (wraps at both ends).
2026-07-31 18:05:23 +08:00

191 lines
4.8 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "app_manager.h"
#include "event.h"
#include "xtell.h"
// 前台与后台应用的运行指针槽位
static WristbandApp *fg_app = NULL;
static WristbandApp *bg_apps[3] = {NULL, NULL, NULL};
/**
* @brief 判断某个应用是否正在后台挂载运行中
*/
static bit IsAppInBackground(WristbandApp *app)
{
u8 i;
if (app == NULL) return 0;
for (i = 0; i < 3; i++) {
if (bg_apps[i] == app) {
return 1;
}
}
return 0;
}
/**
* @brief 初始化应用管理器
*/
void AppManager_Init(void)
{
u8 i;
fg_app = NULL;
for (i = 0; i < 3; i++) {
bg_apps[i] = NULL;
}
XTELL_LOG("[AM] AppManager Initialized.\r\n");
}
/**
* @brief 动态将一个应用切换至前台活跃运行
*/
bit AppManager_SwitchToForeground(WristbandApp *app)
{
if (app == NULL) return 0;
XTELL_LOG("[AM] Switch foreground to: ");
XTELL_LOG(app->app_name);
XTELL_LOG("\r\n");
// 1. 若当前有正在运行的前台应用,将其移出前台
if (fg_app != NULL) {
WristbandApp *prev_app = fg_app;
fg_app = NULL; // 物理断开,防止在 onClose 期间被重入轮询
// 若该应用没有在后台运行,需要彻底关闭它以清理外设
if (!IsAppInBackground(prev_app)) {
if (prev_app->onClose != NULL) {
prev_app->onClose();
}
}
}
// 2. 挂载新的前台应用
fg_app = app;
// 3. 启动新应用生命周期
// 若该应用之前在后台运行,不用重新执行 OnStart直接接管前台显示即可
// 否则说明是初次运行,执行 OnStart 进行界面绘制与状态初始化。
if (!IsAppInBackground(app)) {
if (app->OnStart != NULL) {
app->OnStart();
}
}
return 1;
}
/**
* @brief 动态将一个应用挂载到后台槽位
*/
bit AppManager_AttachToBackground(WristbandApp *app)
{
u8 i;
if (app == NULL) return 0;
// 若已经存在于后台,直接返回成功
if (IsAppInBackground(app)) {
return 1;
}
// 寻找空闲的后台槽位进行挂载
for (i = 0; i < 3; i++) {
if (bg_apps[i] == NULL) {
bg_apps[i] = app;
XTELL_LOG("[AM] Attach to background slot [");
XTELL_LOG_HEX8(i);
XTELL_LOG("]: ");
XTELL_LOG(app->app_name);
XTELL_LOG("\r\n");
// 若该应用既不处于前台,也没在后台初始化,执行其 OnStart()
if (fg_app != app) {
if (app->OnStart != NULL) {
app->OnStart();
}
}
return 1;
}
}
XTELL_LOG("[AM] ERROR: Background slots full!\r\n");
return 0; // 槽位满,挂载失败
}
/**
* @brief 动态将一个应用从后台卸载
*/
void AppManager_DetachFromBackground(WristbandApp *app)
{
u8 i;
if (app == NULL) return;
for (i = 0; i < 3; i++) {
if (bg_apps[i] == app) {
bg_apps[i] = NULL;
XTELL_LOG("[AM] Detach from background: ");
XTELL_LOG(app->app_name);
XTELL_LOG("\r\n");
// 如果该应用未在前台运行,需要彻底关闭它
if (fg_app != app) {
if (app->onClose != NULL) {
app->onClose();
}
}
return;
}
}
}
/**
* @brief 向前台活跃应用派发事件
*/
void AppManager_DispatchEvent(SystemEvent evt)
{
if (fg_app != NULL && fg_app->onEvent != NULL) {
fg_app->onEvent(&evt);
}
}
/**
* @brief 主循环中轮询前后台所有应用
*/
void AppManager_RunActiveApp(void)
{
u8 i;
// 1. 轮询前台活跃应用
if (fg_app != NULL && fg_app->onRun != NULL) {
fg_app->onRun();
}
// 2. 轮询后台挂载的应用
for (i = 0; i < 3; i++) {
if (bg_apps[i] != NULL && bg_apps[i]->onRun != NULL) {
bg_apps[i]->onRun();
}
}
}
/**
* @brief 获取当前前台活跃应用的唯一 ID
*/
u8 AppManager_GetActiveAppID(void)
{
if (fg_app != NULL) {
return fg_app->app_id;
}
return 0xFF;
}
// 供遗留代码兼容性引用的 APIStartApp 映射至 SwitchToForeground 动态方式
void AppManager_StartApp(AppID app_id)
{
if (app_id == APP_ID_HOME) AppManager_SwitchToForeground(&home_app);
else if (app_id == APP_ID_MENU) AppManager_SwitchToForeground(&menu_app);
else if (app_id == APP_ID_SET_CLOCK) AppManager_SwitchToForeground(&set_clock_app);
else if (app_id == APP_ID_PAIR) AppManager_SwitchToForeground(&pair_app);
else if (app_id == APP_ID_IPC_BIND) AppManager_SwitchToForeground(&ipc_bind_app);
else if (app_id == APP_ID_ALARM) AppManager_SwitchToForeground(&alarm_app);
else if (app_id == APP_ID_SOS) AppManager_SwitchToForeground(&sos_app);
}