通知模型补全(agent-107: SDK icon+update_progress / IPC urgency+MSG_NOTIFY_PROGRESS / 存储 urgency+icon回退+progress / CSV加3列) + 全屏收合视觉+滑条(agent-108)
This commit is contained in:
1 parent
b3e57d4b55
commit
e8fa373a2f
18 files changed
+410
-56
No files matched your search
@@ -44,6 +44,7 @@ enum {
|
||||
MSG_NOTIFY_ACK = 0x0021, /* ↓ 落库结果 */
|
||||
MSG_NOTIFY_DISMISS = 0x0022, /* ↑ 撤下通知 */
|
||||
MSG_NOTIFY_LIST = 0x0023, /* ↓ 下发通知清单(重同步) */
|
||||
MSG_NOTIFY_PROGRESS = 0x0024,/* ↑ 实时更新通知进度(仅改内存活跃槽,不落盘) */
|
||||
MSG_ACT_DECLARE = 0x0030, /* ↑ (重)声明路由表 */
|
||||
MSG_ACT_QUERY = 0x0031, /* ↑ 解析 activity 链路 */
|
||||
MSG_ACT_RESULT = 0x0032, /* ↓ 返回链路 */
|
||||
@@ -134,9 +135,11 @@ typedef struct m_act_declare {
|
||||
} m_act_declare_t;
|
||||
|
||||
/* MSG_NOTIFY ↑:推送/更新通知(freedesktop 对齐 + MeowOS 扩展;定长 LE)。
|
||||
* - summary/body/app_icon/expire_timeout 直通 freedesktop 字段;
|
||||
* - summary/body/app_icon/expire_timeout 直通 freedesktop 字段;app_icon 即 SDK `icon`
|
||||
* (icon name 或 path;映射为空串时 launcher 用该 app 默认图标回退);
|
||||
* - target_activity/progress 为 MeowOS 专有(存储层映射为 hints 的
|
||||
* meow.target_activity / meow.progress);
|
||||
* - urgency 为 MeowOS 扩展(0-3,缺省 NORMAL;旧调用置 0=旧 pad 位);
|
||||
* - actions[]/hints{} v1 走定长空串(M2b D-Bus 桥再填);media/ongoing 标记。
|
||||
* 宿主:NOTIFICATION_SYSTEM.md §2/§4 + FRAMEWORK_DESIGN §4/附C。 */
|
||||
typedef struct m_notify {
|
||||
@@ -144,14 +147,22 @@ typedef struct m_notify {
|
||||
char category[24]; /* 通知类型(媒体通知="media") */
|
||||
char summary[64];
|
||||
char body[96];
|
||||
char app_icon[48];
|
||||
char app_icon[48]; /* SDK icon;空=launcher 按 app_id 回退 app 图标 */
|
||||
char target_activity[MEOW_ACT_MAX]; /* MeowOS:点击跳转目标 activity */
|
||||
uint32_t replaces_id; /* 0=新建;>0=替换已存在通知 */
|
||||
uint32_t expire_timeout; /* ms;0xFFFFFFFF=ONGOING 常驻 */
|
||||
int32_t progress; /* MeowOS:0-100;<0=无 */
|
||||
uint8_t media; /* 1=媒体通知 */
|
||||
uint8_t ongoing; /* 1=常驻通知 */
|
||||
uint16_t pad;
|
||||
} m_notify_t; /* 定长 LE */
|
||||
uint8_t urgency; /* 0-3(低/普通/高/紧急);越界由 SDK 钳位缺省 NORMAL;旧调用置 0=旧 pad 位 → LOW/静默 */
|
||||
uint8_t pad;
|
||||
} m_notify_t; /* 定长 LE;尾部 4 字节(media/ongoing/urgency/pad)与旧版 pad 等宽 */
|
||||
|
||||
/* MSG_NOTIFY_PROGRESS ↑:实时更新一条已存在通知的进度(仅改 launcher 内存活跃槽,
|
||||
* 不落盘——TF 快照只在创建/结束(100% 或 dismiss)写)。 */
|
||||
typedef struct m_notify_progress {
|
||||
uint32_t nid; /* launcher 分配的通知 id(MSG_NOTIFY_ACK.status) */
|
||||
int32_t progress; /* 0-100;<0=清除进度条 */
|
||||
} m_notify_progress_t; /* 8 B */
|
||||
|
||||
#endif /* MEOW_IPC_H */
|
||||
Binary file not shown.
@@ -32,9 +32,13 @@
|
||||
* app_id="system" 同走配额);TTL 10s 转储钩子(§8,TF 落盘 M2c 后置,预留 notif_dump_cb);
|
||||
* 满丢该类型最旧条(ring 语义);ONGOING 常驻(转储后仍保留,服务死随清)。
|
||||
* - meow_settings 默认表新增 notify.categories/category_quota/media_quota(B2a 后端)。
|
||||
* - IPC:ipc_dispatch 新增 MSG_NOTIFY→MSG_NOTIFY_ACK、MSG_NOTIFY_DISMISS→MSG_ACK;
|
||||
* - IPC:ipc_dispatch 新增 MSG_NOTIFY→MSG_NOTIFY_ACK、MSG_NOTIFY_DISMISS→MSG_ACK、
|
||||
* MSG_NOTIFY_PROGRESS→MSG_ACK(实时进度仅改内存活跃槽,不落盘);
|
||||
* MSG_NOTIFY_LIST 为守护→App 下行(M5 接入)。meow_ipc 测试钩子新增 notify/dismiss 子命令。
|
||||
* - meow_notif_before_poweroff() 挂入 sys_do_reboot():正常电源事件前先转储未上卡通知再清板载。
|
||||
* - 通知模型补全(2026-08-30,用户定案):SDK→IPC→存储→dump 全链路补 urgency(0-3)/
|
||||
* icon(空→按 app_id 回退该 app 默认图标);progress 实时仅改内存(MSG_NOTIFY_PROGRESS),
|
||||
* TF 快照只在创建/结束(100% 或 dismiss)(meow_notif_dump_tf 写当时值)。
|
||||
*
|
||||
* B2a 统一系统设置后端(2026-08-29):
|
||||
* - 新增 meow_settings.h/.c(SYSTEM_SETTINGS.md §3/MEOWOS_API §2):全部系统设置
|
||||
@@ -2460,6 +2464,17 @@ static void notif_dump_tf_placeholder(const meow_notif_t *n)
|
||||
meow_notif_dump_tf(n);
|
||||
}
|
||||
|
||||
/* ---- 通知图标回退回调(用户定案:icon 为空 → 用该 app 默认图标)----
|
||||
* post 的 app_icon 为空时调用(meow_notif.c 的 notif_resolve_icon),按 app_id 在 app
|
||||
* 注册表(scan_apps 静态 app.json 的 icon)里找该 app 的图标(name/path 或空)。 */
|
||||
static int notif_app_icon_fallback(const char *app_id, char *out, size_t outsz)
|
||||
{
|
||||
if (!app_id || !app_id[0] || !out || outsz == 0) return 0;
|
||||
struct meow_acct *a = meow_acct_find(app_id);
|
||||
if (a && a->icon[0]) { snprintf(out, outsz, "%s", a->icon); return 1; }
|
||||
return 0; /* 无注册/无图标 → 保持空(通知中心 UI 再回退) */
|
||||
}
|
||||
|
||||
/* 启动加载(M2.11/M2.24):统一由 meow_settings 后端读 settings.conf;
|
||||
* 此处仅注册监听(一次性)+ 把 backend 当前值同步到缓存全局。重载(应用期
|
||||
* watchdog 可能改音量)等价于 meow_settings_init() + 手动同步。 */
|
||||
@@ -2469,6 +2484,7 @@ static void settings_load(void)
|
||||
meow_settings_init(); /* 幂等:注册默认 + 读 settings.conf */
|
||||
meow_notif_init(); /* M2a:通知存储初始化(幂等,读 notify.* 配额) */
|
||||
meow_notif_set_dump_cb(notif_dump_tf_placeholder); /* 通知#6:挂 dump 回调 = TF 转储(meow_notif_dump_tf:一行 CSV 追加 + 10MB 滚动截断) */
|
||||
meow_notif_set_icon_cb(notif_app_icon_fallback); /* icon 空 → 按 app_id 回退该 app 默认图标(app.json icon) */
|
||||
if (!settings_cache_registered) {
|
||||
meow_settings_add_listener(settings_sync_cache_listener);
|
||||
settings_cache_registered = 1;
|
||||
@@ -6951,6 +6967,7 @@ static void ipc_dispatch(struct meow_ipc_msg *m)
|
||||
snprintf(n.target_activity, sizeof n.target_activity, "%s", mn.target_activity);
|
||||
n.expire_timeout = mn.expire_timeout;
|
||||
n.progress = mn.progress;
|
||||
n.urgency = mn.urgency; /* 0-3(低/普通/高/紧急);旧调用=0 → LOW(静默),新调用缺省 NORMAL */
|
||||
n.ongoing = mn.ongoing;
|
||||
n.media = mn.media;
|
||||
uint32_t nid = 0;
|
||||
@@ -6973,6 +6990,19 @@ static void ipc_dispatch(struct meow_ipc_msg *m)
|
||||
logmsg("ipc: NOTIFY_DISMISS id=%u rc=%d -> ACK", id, rc);
|
||||
break;
|
||||
}
|
||||
case MSG_NOTIFY_PROGRESS: {
|
||||
m_notify_progress_t p;
|
||||
memset(&p, 0, sizeof p);
|
||||
memcpy(&p, m->payload, m->plen < (uint16_t)sizeof p ? m->plen : (uint16_t)sizeof p);
|
||||
/* 实时进度:仅改 launcher 内存活跃槽(不落盘);progress>=100 视为结束 → 里程碑落盘 */
|
||||
int rc = meow_notif_update_progress(p.nid, p.progress);
|
||||
m_ack_t a;
|
||||
a.code = (rc == NOTIF_OK) ? MEOW_OK : (uint32_t)rc;
|
||||
a.status = 0;
|
||||
ipc_send(m->fd, MSG_ACK, m->req_id, &a, sizeof a);
|
||||
logmsg("ipc: NOTIFY_PROGRESS nid=%u p=%d rc=%d -> ACK", p.nid, p.progress, rc);
|
||||
break;
|
||||
}
|
||||
case MSG_NOTIFY_LIST:
|
||||
/* ↓ 守护→App 下发通知清单(重同步)。本帧不来自客户端(无请求类型),故不在此应答;
|
||||
* 需用时由守护主动经 meow_notif_serialize_list() + ipc_send 下发(M5 通知中心接入)。 */
|
||||
|
||||
@@ -58,6 +58,7 @@ static int g_cat_quota = MEOW_NOTIF_CAT_QUOTA_DEF;
|
||||
static int g_media_quota = MEOW_NOTIF_MEDIA_QUOTA_DEF;
|
||||
|
||||
static meow_notif_dump_cb g_dump_cb = NULL;
|
||||
static meow_notif_icon_cb g_icon_cb = NULL;
|
||||
static int g_inited = 0;
|
||||
|
||||
/* ---- 日志(自包含,不依赖 launcher 的 logmsg;前缀 "notif:")---- */
|
||||
@@ -94,14 +95,27 @@ static void n_strncpy(char *dst, const char *src, size_t len)
|
||||
dst[len - 1] = 0;
|
||||
}
|
||||
|
||||
/* app_icon 回退(用户定案):post 的 app_icon 为空时,经 g_icon_cb(app_id) 填该 app 默认图标;
|
||||
* 无回调/回调未填 → 保持空(通知中心 UI 再回退)。每条可独立图标(非空即用)。 */
|
||||
static void notif_resolve_icon(meow_notif_t *s)
|
||||
{
|
||||
if (!g_icon_cb) return;
|
||||
if (s->app_icon[0]) return;
|
||||
char tmp[MEOW_NOTIF_ICON_MAX];
|
||||
if (g_icon_cb(s->app_id, tmp, sizeof tmp) > 0 && tmp[0])
|
||||
n_strncpy(s->app_icon, tmp, sizeof s->app_icon);
|
||||
}
|
||||
|
||||
/* ================= TF 卡转储(通知#6 / M2c,NOTIFICATION_SYSTEM.md §5/§8)=================
|
||||
* 每条通知一行 CSV 追加到 <卡根>/MeowOS/notifications/notifications.log,并按日志总大小
|
||||
* 滚动截断(保留最新 10MB)。系统文件统一放 <卡根>/MeowOS/ 下(TF_CARD_LAYOUT.md)。 */
|
||||
* 滚动截断(保留最新 10MB)。系统文件统一放 <卡根>/MeowOS/ 下(TF_CARD_LAYOUT.md)。
|
||||
* 字段见 tf_line():timestamp,id,app_id,app_icon,category,urgency,progress,title,body,
|
||||
* target_activity,ongoing,media,dismissed(CSV 转义/去换行)。 */
|
||||
#define MEOW_NOTIF_TF_DIR "/usr/data/mnt/sd_0/MeowOS/notifications"
|
||||
#define MEOW_NOTIF_TF_LOG MEOW_NOTIF_TF_DIR "/notifications.log"
|
||||
#define MEOW_NOTIF_LOG_MAX_BYTES ((size_t)10 * 1024 * 1024) /* §8:保留最新 10MB */
|
||||
|
||||
/* 单行 CSV 上限:app_id(32)/category(24)/summary(64)/body(96)/target_activity(48)
|
||||
/* 单行 CSV 上限:app_id(32)/category(24)/summary(64)/body(96)/app_icon(48)/target_activity(48)
|
||||
* 全引号转义后仍远小于此。 */
|
||||
#define MEOW_NOTIF_CSV_LINE_MAX 1024
|
||||
|
||||
@@ -155,26 +169,31 @@ static void csv_field(char *dst, size_t dstsz, const char *src)
|
||||
}
|
||||
|
||||
/* 生成一行 CSV:
|
||||
* timestamp,id,app_id,category,urgency,title,body,target_activity,ongoing,media,dismissed
|
||||
* title<-summary;urgency:meow_notif 结构无该字段(保持既有 API),列值留空 ""。
|
||||
* 返回写入长度(不含 NUL)。 */
|
||||
* timestamp,id,app_id,app_icon,category,urgency,progress,title,body,target_activity,ongoing,media,dismissed
|
||||
* title<-summary;app_icon<-SDK icon(空→储存时已按 app_id 回退 app 图标);urgency 0-3(缺省 NORMAL);
|
||||
* progress 写当前值(-1=无进度)。返回写入长度(不含 NUL)。 */
|
||||
static size_t tf_line(const meow_notif_t *n, char *buf, size_t bufsz)
|
||||
{
|
||||
char a_app [2 + 2 * MEOW_NOTIF_APP_ID_MAX + 2];
|
||||
char a_cat [2 + 2 * MEOW_NOTIF_CAT_MAX + 2];
|
||||
char a_sum [2 + 2 * MEOW_NOTIF_SUMMARY_MAX + 2];
|
||||
char a_body[2 + 2 * MEOW_NOTIF_BODY_MAX + 2];
|
||||
char a_icon[2 + 2 * MEOW_NOTIF_ICON_MAX + 2];
|
||||
char a_tgt [2 + 2 * MEOW_NOTIF_ACTIVITY_MAX + 2];
|
||||
csv_field(a_app, sizeof a_app, n->app_id);
|
||||
csv_field(a_cat, sizeof a_cat, n->category);
|
||||
csv_field(a_sum, sizeof a_sum, n->summary);
|
||||
csv_field(a_body, sizeof a_body, n->body);
|
||||
csv_field(a_icon, sizeof a_icon, n->app_icon);
|
||||
csv_field(a_tgt, sizeof a_tgt, n->target_activity);
|
||||
int r = snprintf(buf, bufsz,
|
||||
"%llu,%u,%s,%s,\"\",%s,%s,%s,%u,%u,%u\n",
|
||||
"%llu,%u,%s,%s,%s,%u,%d,%s,%s,%s,%u,%u,%u\n",
|
||||
(unsigned long long)n->created_ms,
|
||||
(unsigned)n->id,
|
||||
a_app, a_cat, a_sum, a_body, a_tgt,
|
||||
a_app, a_icon, a_cat,
|
||||
(unsigned)n->urgency,
|
||||
(int)n->progress,
|
||||
a_sum, a_body, a_tgt,
|
||||
n->ongoing ? 1u : 0u,
|
||||
n->media ? 1u : 0u,
|
||||
n->dismissed ? 1u : 0u);
|
||||
@@ -477,8 +496,10 @@ int meow_notif_post(const meow_notif_t *n, uint32_t *out_id)
|
||||
n_strncpy(s->target_activity, n->target_activity, sizeof s->target_activity);
|
||||
s->expire_timeout = n->expire_timeout;
|
||||
s->progress = n->progress;
|
||||
s->urgency = n->urgency;
|
||||
s->ongoing = n->ongoing;
|
||||
s->media = n->media;
|
||||
notif_resolve_icon(s); /* 空 app_icon → app 图标回退 */
|
||||
if (out_id) *out_id = s->id;
|
||||
notif_log("post: UPDATE id=%u app=%s cat=%s", s->id, s->app_id, s->category);
|
||||
return NOTIF_OK;
|
||||
@@ -533,10 +554,12 @@ int meow_notif_post(const meow_notif_t *n, uint32_t *out_id)
|
||||
n_strncpy(s->target_activity, n->target_activity, sizeof s->target_activity);
|
||||
s->expire_timeout = n->expire_timeout;
|
||||
s->progress = n->progress;
|
||||
s->urgency = n->urgency;
|
||||
s->ongoing = n->ongoing;
|
||||
s->media = media ? 1 : 0;
|
||||
s->created_ms = wall_ms();
|
||||
s->dump_at_ms = mono_ms() + MEOW_NOTIF_TTL_MS; /* §8:创建 10s 转储 */
|
||||
notif_resolve_icon(s); /* 空 app_icon → app 图标回退 */
|
||||
s->active = 1;
|
||||
if (out_id) *out_id = s->id;
|
||||
notif_log("post: NEW id=%u app=%s cat=%s media=%d ongoing=%d", s->id, s->app_id,
|
||||
@@ -558,6 +581,26 @@ int meow_notif_dismiss(uint32_t id)
|
||||
return NOTIF_OK;
|
||||
}
|
||||
|
||||
/* ---- 实时更新进度:仅改内存活跃槽;progress>=100 视为「结束」→ 立即归档(里程碑落盘)---- */
|
||||
int meow_notif_update_progress(uint32_t id, int progress)
|
||||
{
|
||||
if (!g_inited) return NOTIF_ERR_NOTINIT;
|
||||
int p = (progress < -1) ? -1 : (progress > 100 ? 100 : progress); /* 钳位 [-1,100] */
|
||||
int idx = slot_by_id(id);
|
||||
if (idx < 0) return NOTIF_ERR_NOENT;
|
||||
meow_notif_t *s = &g_slots[idx];
|
||||
s->progress = p; /* 实时:只改内存活跃槽,不落盘 */
|
||||
if (p >= 100) { /* 结束(100%):里程碑落盘 + 释放非 ONGOING */
|
||||
int was_ongoing = s->ongoing;
|
||||
archive_one(s); /* 此刻快照(progress=100 写入 dump) */
|
||||
if (!was_ongoing) slot_release(idx);
|
||||
else notif_log("progress: id=%u reached 100%%, archived (kept for UI)", id);
|
||||
} else {
|
||||
notif_log("progress: id=%u p=%d (memory only)", id, p);
|
||||
}
|
||||
return NOTIF_OK;
|
||||
}
|
||||
|
||||
/* ---- count ---- */
|
||||
int meow_notif_count(const char *app_id)
|
||||
{
|
||||
@@ -641,6 +684,12 @@ void meow_notif_set_dump_cb(meow_notif_dump_cb cb)
|
||||
g_dump_cb = cb; /* NULL=恢复默认(无回调,仅日志;M2c 接 TF) */
|
||||
}
|
||||
|
||||
/* ---- 设置图标回退回调 ---- */
|
||||
void meow_notif_set_icon_cb(meow_notif_icon_cb cb)
|
||||
{
|
||||
g_icon_cb = cb; /* NULL=禁用(空 app_icon 保持空,通知中心 UI 再回退) */
|
||||
}
|
||||
|
||||
/* ---- MSG_NOTIFY_LIST 序列化(守护→App 下发清单;M5 通知中心/重同步用)----
|
||||
* 负载: [u16 count] + count 条 定长 meow_notif 记录(records 以 app_id/category/
|
||||
* summary/body/app_icon/target_activity 为主,精简 payload,适配 MEOW_IPC_CARGO)。 */
|
||||
@@ -690,8 +739,8 @@ void meow_notif_dump(const char *log_prefix)
|
||||
for (int i = 0; i < MEOW_NOTIF_MAX_SLOTS; i++) {
|
||||
meow_notif_t *s = &g_slots[i];
|
||||
if (!s->active) continue;
|
||||
notif_log("%s slot%02d id=%u app=%s cat=%s sum=%s ong=%d media=%d dumped=%d",
|
||||
notif_log("%s slot%02d id=%u app=%s cat=%s sum=%s ong=%d media=%d urg=%u dumped=%d",
|
||||
lp, i, s->id, s->app_id, s->category, s->summary,
|
||||
s->ongoing, s->media, s->dumped);
|
||||
s->ongoing, s->media, s->urgency, s->dumped);
|
||||
}
|
||||
}
|
||||
@@ -66,14 +66,15 @@ typedef struct meow_notif {
|
||||
char category[MEOW_NOTIF_CAT_MAX]; /* 通知类型(媒体通知="media") */
|
||||
char summary[MEOW_NOTIF_SUMMARY_MAX]; /* 标题(单行) */
|
||||
char body[MEOW_NOTIF_BODY_MAX]; /* 正文(可多行) */
|
||||
char app_icon[MEOW_NOTIF_ICON_MAX]; /* 图标 icon name 或 path */
|
||||
char app_icon[MEOW_NOTIF_ICON_MAX]; /* 图标 icon name 或 path(SDK icon;空→app 图标回退) */
|
||||
char actions[MEOW_NOTIF_ACTIONS_MAX]; /* actions[](NUL 分隔;v1 空,M2b 填) */
|
||||
char hints[MEOW_NOTIF_HINTS_MAX]; /* hints{} 原始 "k=v;..." 串(v1 空,M2b 填) */
|
||||
uint32_t expire_timeout; /* 超时 ms;0xFFFFFFFF=ONGOING 常驻 */
|
||||
uint64_t created_ms; /* 创建时刻(墙钟 epoch ms,供归档/显示) */
|
||||
uint64_t dump_at_ms; /* 转储时刻(monotonic ms,TTL=创建+10s;ONGOING 亦到此转储) */
|
||||
int32_t progress; /* MeowOS:0-100 进度;<0=无 */
|
||||
int32_t progress; /* MeowOS:0-100 进度;<0=无(实时仅改内存,落盘只在创建/结束) */
|
||||
char target_activity[MEOW_NOTIF_ACTIVITY_MAX]; /* MeowOS:点击跳转目标 activity */
|
||||
uint8_t urgency; /* 紧急度 0-3(低/普通/高/紧急);缺省 NORMAL */
|
||||
uint8_t ongoing; /* 1=常驻通知(服务死随清) */
|
||||
uint8_t media; /* 1=媒体通知(走 notify.media_quota) */
|
||||
uint8_t dismissed; /* 1=已撤下 */
|
||||
@@ -84,6 +85,10 @@ typedef struct meow_notif {
|
||||
/* 转储回调(M2c 落盘 / 本批默认仅日志):通知归档时调用。 */
|
||||
typedef void (*meow_notif_dump_cb)(const meow_notif_t *n);
|
||||
|
||||
/* 图标回退回调:post 的 n->app_icon 为空时调用,按 app_id 填该 app 默认图标(name/path)。
|
||||
* 返回 1=已填 out;0/空=无(通知中心 UI 再回退)。NULL=禁用回退(保持空 app_icon)。 */
|
||||
typedef int (*meow_notif_icon_cb)(const char *app_id, char *out, size_t outsz);
|
||||
|
||||
/* == 生命周期 == */
|
||||
int meow_notif_init(void); /* 幂等;从 meow_settings 读 notify.* 配额(B2a) */
|
||||
int meow_notif_shutdown(void); /* 保存/清理(进程退出前调;本批=归档未转储项) */
|
||||
@@ -119,12 +124,19 @@ void meow_notif_before_poweroff(void);
|
||||
/* 覆盖转储回调(NULL=恢复默认日志;M2c 接 TF 落盘)。 */
|
||||
void meow_notif_set_dump_cb(meow_notif_dump_cb cb);
|
||||
|
||||
/* 覆盖图标回退回调(NULL=禁用,空 app_icon 保持空;M5 通知中心 UI 再回退)。 */
|
||||
void meow_notif_set_icon_cb(meow_notif_icon_cb cb);
|
||||
|
||||
/* 实时更新一条活跃通知的进度(0-100,<0=清除)。仅改内存活跃槽;progress>=100 视为「结束」,
|
||||
* 立即归档(落盘快照)并(非 ONGOING)释放槽——对应「里程碑落盘」。返回 NOTIF_OK/NOTIF_ERR_NOENT。 */
|
||||
int meow_notif_update_progress(uint32_t id, int progress);
|
||||
|
||||
/* == TF 卡转储(通知#6 / M2c,NOTIFICATION_SYSTEM.md §5/§8 + MEOWOS_SPEC_REF §5.3)==
|
||||
* 把 dump_cb 接到 meow_notif_dump_tf 即把每条通知追加为一行 CSV 到
|
||||
* <卡根>/MeowOS/notifications/notifications.log,并按日志总大小滚动截断
|
||||
* (保留最新 10MB,不被电源事件清;启动时 meow_notif_init 先清板载+内存表)。
|
||||
* 字段按 meow_notif 结构(title<-summary;urgency 结构无字段故列留空;均做 CSV
|
||||
* 转义/去换行)。无卡/不可写仅打点降级,绝不崩。 */
|
||||
* 字段按 meow_notif 结构(title<-summary;app_icon<-SDK icon 空→app 图标回退;urgency 0-3;
|
||||
* progress 当前值,-1=无;均做 CSV 转义/去换行)。无卡/不可写仅打点降级,绝不崩。 */
|
||||
int meow_notif_dump_tf(const meow_notif_t *n); /* 0=成功;负=失败(无卡/IO/参数) */
|
||||
void meow_notif_clear_tf(void); /* 开机清板载通知(清空文件,保留目录) */
|
||||
void meow_notif_set_tf_path(const char *path); /* 覆盖转储日志路径;NULL=恢复默认卡路径(宿主单测用) */
|
||||
|
||||
@@ -53,9 +53,37 @@ static void make_notif(meow_notif_t *n, uint32_t id, const char *sum, const char
|
||||
strcpy(n->summary, sum);
|
||||
strcpy(n->body, body);
|
||||
strcpy(n->target_activity, "act");
|
||||
n->urgency = 0; /* 0=LOW(缺省值) */
|
||||
n->progress = -1; /* 无进度 */
|
||||
n->ongoing = 1;
|
||||
}
|
||||
|
||||
/* 回读指定 summary 的活跃通知(meow_notif_list 回调;按 g_find_key 匹配) */
|
||||
static meow_notif_t g_got; static int g_got_found = 0; static char g_find_key[64];
|
||||
static int find_cb(const meow_notif_t *n, void *ctx)
|
||||
{
|
||||
(void)ctx;
|
||||
if (n && strcmp(n->summary, g_find_key) == 0) { g_got = *n; g_got_found = 1; return 1; }
|
||||
return 0;
|
||||
}
|
||||
static meow_notif_t find_notif(const char *summary)
|
||||
{
|
||||
g_got_found = 0; memset(&g_got, 0, sizeof g_got);
|
||||
snprintf(g_find_key, sizeof g_find_key, "%s", summary);
|
||||
meow_notif_list(NULL, find_cb, NULL);
|
||||
return g_got_found ? g_got : (meow_notif_t){0};
|
||||
}
|
||||
|
||||
/* 图标回退 stub:com.app → "app_icon_name";其他 app → 无 */
|
||||
static int stub_icon_cb(const char *app_id, char *out, size_t outsz)
|
||||
{
|
||||
if (app_id && !strcmp(app_id, "com.app")) { snprintf(out, outsz, "app_icon_name"); return 1; }
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* dump_cb 类型是 void,而 meow_notif_dump_tf 返回 int——包一层(同 launcher notif_dump_tf_placeholder) */
|
||||
static void dump_tf_wrapper(const meow_notif_t *n){ (void)meow_notif_dump_tf(n); }
|
||||
|
||||
/* --- 1) CSV 转义 + 去换行 --- */
|
||||
static void test_csv_escape(const char *path)
|
||||
{
|
||||
@@ -71,9 +99,9 @@ static void test_csv_escape(const char *path)
|
||||
char *s = read_all(path, NULL);
|
||||
CHECK(s != NULL, "file exists after dump");
|
||||
const char *expect =
|
||||
"1234567890123,42,\"com.app\",\"general\",\"\",\"Hel\"\"lo, world line2\","
|
||||
"\"Body with\ttab\",\"act\",1,1,0\n"; /* ongoing=1 media=1 dismissed=0 */
|
||||
CHECK(s && strcmp(s, expect) == 0, "CSV line matches expected (escape+newline-strip)");
|
||||
"1234567890123,42,\"com.app\",\"\",\"general\",0,-1,\"Hel\"\"lo, world line2\","
|
||||
"\"Body with\ttab\",\"act\",1,1,0\n"; /* app_icon="" urgency=0 progress=-1 ongoing=1 media=1 dismissed=0 */
|
||||
CHECK(s && strcmp(s, expect) == 0, "CSV line matches expected (escape+newline-strip+new cols)");
|
||||
if (s) { fprintf(stderr, " got: %s", s); fprintf(stderr, " exp: %s", expect); }
|
||||
free(s);
|
||||
}
|
||||
@@ -134,24 +162,92 @@ static void test_init_clear(const char *path)
|
||||
free(s);
|
||||
}
|
||||
|
||||
/* --- 4) icon 回退:app_icon 空 → 按 app_id 用该 app 默认图标;非空 → 保留 --- */
|
||||
static void test_icon_fallback(const char *path)
|
||||
{
|
||||
meow_notif_set_icon_cb(stub_icon_cb);
|
||||
meow_notif_before_poweroff(); /* 清内存表,回到干净态(dump_cb=NULL 不落盘) */
|
||||
meow_notif_set_tf_path(path);
|
||||
meow_notif_set_tf_max(1024 * 1024);
|
||||
|
||||
/* 空 app_icon → 回退 app 图标 */
|
||||
meow_notif_t n;
|
||||
make_notif(&n, 0, "icon-test", "");
|
||||
uint32_t id = 0;
|
||||
CHECK(meow_notif_post(&n, &id) == NOTIF_OK, "icon-fallback post ok");
|
||||
meow_notif_t got = find_notif("icon-test");
|
||||
CHECK(got.id != 0 && !strcmp(got.app_icon, "app_icon_name"), "empty app_icon → app icon fallback");
|
||||
|
||||
/* 非空 app_icon → 独立图标(不回退) */
|
||||
make_notif(&n, 0, "icon-custom", "");
|
||||
n.app_icon[0] = 0; strcpy(n.app_icon, "custom/icon.png");
|
||||
CHECK(meow_notif_post(&n, &id) == NOTIF_OK, "icon-custom post ok");
|
||||
got = find_notif("icon-custom");
|
||||
CHECK(got.id != 0 && !strcmp(got.app_icon, "custom/icon.png"), "non-empty app_icon kept (per-notif icon)");
|
||||
}
|
||||
|
||||
/* --- 5) progress 实时(仅内存)vs 里程碑(100% 落盘 + 释放) --- */
|
||||
static void test_progress_live(const char *path)
|
||||
{
|
||||
meow_notif_set_icon_cb(NULL);
|
||||
meow_notif_set_dump_cb(NULL);
|
||||
meow_notif_before_poweroff(); /* 清内存表,回到干净态 */
|
||||
meow_notif_set_tf_path(path);
|
||||
meow_notif_set_tf_max(1024 * 1024);
|
||||
meow_notif_set_dump_cb(dump_tf_wrapper); /* 归档即写 CSV;验证落盘快照 */
|
||||
|
||||
meow_notif_t n;
|
||||
make_notif(&n, 0, "live", "");
|
||||
n.ongoing = 0; /* 非 ONGOING,便于验证 100% 释放 */
|
||||
n.progress = 0;
|
||||
uint32_t id = 0;
|
||||
CHECK(meow_notif_post(&n, &id) == NOTIF_OK, "progress post ok");
|
||||
CHECK(meow_notif_count(NULL) == 1, "one active after post");
|
||||
|
||||
/* 实时 <100%:仅内存,不落盘 */
|
||||
CHECK(meow_notif_update_progress(id, 70) == NOTIF_OK, "update_progress(70) ok");
|
||||
CHECK(meow_notif_count(NULL) == 1, "still active after live update (not dumped)");
|
||||
long len = 0; char *s = read_all(path, &len);
|
||||
CHECK((s == NULL || len == 0), "no CSV written on live update (<100%)");
|
||||
free(s);
|
||||
|
||||
/* 里程碑 100%:立即归档落盘 + 释放非 ONGOING 槽 */
|
||||
CHECK(meow_notif_update_progress(id, 100) == NOTIF_OK, "update_progress(100) ok");
|
||||
CHECK(meow_notif_count(NULL) == 0, "slot released after 100% milestone");
|
||||
s = read_all(path, &len);
|
||||
CHECK(s != NULL && len > 0, "CSV written on 100% milestone");
|
||||
CHECK(s && strstr(s, ",100,\"live\"") != NULL, "milestone CSV has progress=100 title=live");
|
||||
if (s) fprintf(stderr, " milestone row: %s", s);
|
||||
free(s);
|
||||
|
||||
/* 已释放 id → NOENT */
|
||||
CHECK(meow_notif_update_progress(id, 50) == NOTIF_ERR_NOENT, "update on released id -> NOENT");
|
||||
|
||||
meow_notif_set_dump_cb(NULL); /* 复位 */
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
char tmp[] = "/tmp/meow_notif_tf_test_XXXXXX";
|
||||
char *dir = mkdtemp(tmp);
|
||||
if (!dir) { perror("mkdtemp"); return 2; }
|
||||
|
||||
char p1[512], p2[512], p3[512];
|
||||
char p1[512], p2[512], p3[512], p4[512], p5[512];
|
||||
snprintf(p1, sizeof p1, "%s/csv.log", dir);
|
||||
snprintf(p2, sizeof p2, "%s/trim.log", dir);
|
||||
snprintf(p3, sizeof p3, "%s/init.log", dir);
|
||||
snprintf(p4, sizeof p4, "%s/icon.log", dir);
|
||||
snprintf(p5, sizeof p5, "%s/prog.log", dir);
|
||||
|
||||
CHECK(p1[0] != 0, "tmpdir ready");
|
||||
test_csv_escape(p1);
|
||||
test_trim(p2);
|
||||
test_init_clear(p3);
|
||||
test_icon_fallback(p4);
|
||||
test_progress_live(p5);
|
||||
|
||||
/* 清理:删除日志文件 + 目录(保底) */
|
||||
unlink(p1); unlink(p2); unlink(p3); rmdir(dir);
|
||||
unlink(p1); unlink(p2); unlink(p3); unlink(p4); unlink(p5); rmdir(dir);
|
||||
|
||||
fprintf(stderr, "\n%s (failures=%d)\n", g_fail ? "== TEST FAILED ==" : "== TEST PASSED ==", g_fail);
|
||||
return g_fail ? 1 : 0;
|
||||
|
||||
Reference in new issue
Block a user