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).
191 lines
4.8 KiB
C
191 lines
4.8 KiB
C
#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;
|
||
}
|
||
|
||
// 供遗留代码兼容性引用的 API:StartApp 映射至 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);
|
||
}
|