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