diff --git a/meow_notify_ipc.c b/meow_notify_ipc.c index a4c768b..6486fed 100644 --- a/meow_notify_ipc.c +++ b/meow_notify_ipc.c @@ -6,13 +6,15 @@ * meow_ipc.h(同源一份、杜绝两端漂移): * - 帧头 16B:magic=0x4D4F5701 / version=1 / type / req_id / len(全小端 LE,mipsel 原生); * - MSG_NOTIFY(0x0020) ↑ 负载 = m_notify_t;MSG_NOTIFY_ACK(0x0021) ↓ 负载 = m_ack_t(status=nid); - * - MSG_NOTIFY_DISMISS(0x0022) ↑ 负载 = uint32 nid。 + * - MSG_NOTIFY_DISMISS(0x0022) ↑ 负载 = uint32 nid; + * - MSG_NOTIFY_PROGRESS(0x0024) ↑ 负载 = m_notify_progress_t(nid+progress,实时进度仅内存)。 * * 设计要点: * - 只做「一次性 connect + send + read ACK」,不维护连接/订阅循环(与 meow_ipc.c 客户端同型); * - 映射层(SDK 枚举 ↔ IPC 字段)集中在本文件:分类枚举 CAT_* ↔ 字符串 * "general"/"message"/"email"/"event"/"progress"/"media";timeout_ms → expire_timeout: * >0 该毫秒、0=不自动过期、-1(ONGOING)=0xFFFFFFFF(并置 ongoing=1);媒体类型置 media=1; + * SDK `icon` → app_icon(空则 launcher 按 app_id 回退 app 图标);urgency 0-3 直通(越界钳位 NORMAL); * - 优雅降级:无 socket(connect 失败)/写失败/ACK 超时(SO_RCVTIMEO)/非 ACK 应答 → * 返回 0(调用方 meow_ui_notify.c 保留本地 id 兜底),绝不崩; * - 线程安全:app_id 经 pthread_once 缓存;socket 路径每次调用读 env MEOW_IPC_SOCK 覆盖 @@ -82,8 +84,8 @@ static uint32_t timeout_to_expire(int timeout_ms) return (uint32_t)timeout_ms; /* 0(NEVER)/ >0(毫秒) */ } -/* 构建 IPC 负载 m_notify_t(app_icon/actions/hints v1 留空,M2b D-Bus 桥再填)。 - * 仅在持有调用者互斥之外、发送前调用(纯 CPU 拷贝,不阻塞)。 */ +/* 构建 IPC 负载 m_notify_t(app_icon<->SDK icon;urgency 0-3;actions/hints v1 留空, + * M2b D-Bus 桥再填)。仅在持有调用者互斥之外、发送前调用(纯 CPU 拷贝,不阻塞)。 */ static void build_m_notify(const meow_notify *n, uint32_t replaces_id, m_notify_t *mn) { memset(mn, 0, sizeof *mn); @@ -91,6 +93,7 @@ static void build_m_notify(const meow_notify *n, uint32_t replaces_id, m_notify_ snprintf(mn->category, sizeof mn->category, "%s", cat_to_str(n->cat)); snprintf(mn->summary, sizeof mn->summary, "%s", n->title ? n->title : ""); snprintf(mn->body, sizeof mn->body, "%s", n->body ? n->body : ""); + snprintf(mn->app_icon, sizeof mn->app_icon, "%s", n->icon ? n->icon : ""); snprintf(mn->target_activity, sizeof mn->target_activity, "%s", n->target_activity ? n->target_activity : ""); mn->replaces_id = replaces_id; /* 0=新建;>0=更新(不存在则落为新建,见 launcher) */ @@ -98,6 +101,9 @@ static void build_m_notify(const meow_notify *n, uint32_t replaces_id, m_notify_ mn->progress = (n->progress < -1) ? -1 : (n->progress > 100 ? 100 : n->progress); mn->media = (n->cat == MEOW_NOTIFY_CAT_MEDIA) ? 1 : 0; mn->ongoing = (n->timeout_ms == MEOW_NOTIFY_TIMEOUT_ONGOING) ? 1 : 0; + /* urgency:越界钳位到 NORMAL(缺省);0-3 直通(launcher 侧再回退缺省) */ + mn->urgency = ((int)n->urgency >= 0 && (int)n->urgency < (int)MEOW_NOTIFY_URG_COUNT) + ? (uint8_t)n->urgency : (uint8_t)MEOW_NOTIFY_URG_NORMAL; } /* ============================================================ */ @@ -241,3 +247,36 @@ void meow_notify_ipc_dismiss(uint32_t nid) } close(fd); } + +void meow_notify_ipc_update_progress(uint32_t nid, int progress) +{ + int fd; + if (nid == 0) return; + m_notify_progress_t p; + memset(&p, 0, sizeof p); + p.nid = nid; + p.progress = (progress < -1) ? -1 : (progress > 100 ? 100 : progress); + fd = conn_path(ipc_sock_path()); + if (fd < 0) return; /* 无 socket:静默 */ + set_ack_timeout(fd); + if (!send_frame(fd, MSG_NOTIFY_PROGRESS, 0x4E4F5450u /* "NOTP" */, &p, (uint32_t)sizeof p)) { + close(fd); /* 写失败:静默 */ + return; + } + /* 读回应答(launcher 对 PROGRESS 回 MSG_ACK,best-effort;读回确保 launcher 已处理完) */ + uint8_t rhdr[MEOW_IPC_HDR_LEN]; + if (read_all(fd, rhdr, sizeof rhdr)) { + uint32_t rmagic, rlen; + uint16_t rver, rtype; + memcpy(&rmagic, rhdr + 0, 4); + memcpy(&rver, rhdr + 4, 2); + memcpy(&rtype, rhdr + 6, 2); + memcpy(&rlen, rhdr + 12, 4); + if (rmagic == MEOW_IPC_MAGIC && rver == MEOW_IPC_VERSION && + rtype == MSG_ACK && rlen >= sizeof(m_ack_t)) { + uint8_t pay[sizeof(m_ack_t)]; + (void)read_all(fd, pay, sizeof(m_ack_t)); /* 读到即确认;内容不关心 */ + } + } + close(fd); +} diff --git a/meow_notify_ipc.h b/meow_notify_ipc.h index c1da243..f02a5a0 100644 --- a/meow_notify_ipc.h +++ b/meow_notify_ipc.h @@ -4,9 +4,10 @@ * 定位(任务 #5 SDK↔IPC 桥关键断点):让框架 app 调 meow_notify_send() 的通知真正经 * AF_UNIX IPC 送到 launcher(meow_launcherd)中央存储(meow_notif.c 的 meow_notif_post), * 通知中心(阶段 B · M5)才能显示。消息通道(FRAMEWORK_DESIGN §1 / meow_ipc.h): - * - MSG_NOTIFY 0x0020 ↑:推送/更新通知(负载 m_notify_t); + * - MSG_NOTIFY 0x0020 ↑:推送/更新通知(负载 m_notify_t;含 SDK icon→app_icon、urgency); * - MSG_NOTIFY_ACK 0x0021 ↓:落库结果(m_ack_t,status=launcher 分配的通知 id nid); - * - MSG_NOTIFY_DISMISS 0x0022 ↑:撤下通知(负载 uint32 nid)。 + * - MSG_NOTIFY_DISMISS 0x0022 ↑:撤下通知(负载 uint32 nid); + * - MSG_NOTIFY_PROGRESS 0x0024 ↑:实时更新通知进度(负载 m_notify_progress_t,仅改内存活跃槽)。 * * 约定:只做「一次性发送 + 读 ACK」,不造 IPC 订阅循环;SDK 本地活动表(meow_ui_notify.c) * 保留(app 进程内即时查询/配额自检),真正的通知中心数据在 launcher 中央存储,二者以 @@ -38,6 +39,10 @@ uint32_t meow_notify_ipc_send(const meow_notify *n, uint32_t replaces_id); * 静默(不崩、不回读 ACK)。 */ void meow_notify_ipc_dismiss(uint32_t nid); +/* 一次性发送 MSG_NOTIFY_PROGRESS 更新 launcher 侧已存在通知的进度(仅改内存活跃槽, + * 不落盘)。best-effort:写失败/无 socket 静默。 */ +void meow_notify_ipc_update_progress(uint32_t nid, int progress); + #ifdef __cplusplus } #endif diff --git a/meow_ui.h b/meow_ui.h index 298e379..3614006 100644 --- a/meow_ui.h +++ b/meow_ui.h @@ -297,12 +297,13 @@ typedef enum { } meow_notify_urgency; /* 发送负载(对齐 freedesktop Notify() 字段;SDK 内部复制副本,不持 app 外部指针, - * app 只需保证 title/body/target_activity 指针在调用返回前有效)。 */ + * app 只需保证 icon/title/body/target_activity 指针在调用返回前有效)。 */ typedef struct meow_notify { meow_notify_category cat; /* 类型(未声明则按 4 种上限自动注册) */ - meow_notify_urgency urgency; /* 紧急度 */ + meow_notify_urgency urgency; /* 紧急度(缺省 MEOW_NOTIFY_URG_NORMAL;越界钳位) */ const char *title; /* summary:标题,单行(必填,NULL 则发送失败) */ const char *body; /* body:正文,可多行(可 NULL) */ + const char *icon; /* app_icon:图标 icon name 或 path;NULL/空 = 用该 app 默认图标(launcher 按 app_id 回退) */ int timeout_ms; /* 见上超时语义;推荐 MEOW_NOTIFY_TIMEOUT_* */ const char *target_activity; /* 通知中心点击跳转的 Activity 名(可 NULL;M3 导航预留) */ int progress; /* 进度 0-100;-1=不显示进度条(缺省 -1) */ @@ -318,6 +319,11 @@ uint32_t meow_notify_send(const meow_notify *n); /* 取消一条通知。1=成功;0=未找到(id 无效/已过期/已取消)。 */ int meow_notify_cancel(uint32_t id); +/* 实时更新一条活动通知的进度(0-100,-1=清除进度条)。local_id=meow_notify_send 的返回值。 + * 语义(用户定案):仅改 SDK 内部活动槽 + 经 IPC 通知 launcher 改**内存活跃槽**;不落盘—— + * TF 快照只在「创建时 / 结束(100% 或 dismiss)」触发。返回 1=找到并更新;0=未找到/非法。 */ +int meow_notify_update_progress(uint32_t local_id, int progress); + /* 查询当前活动(未过期、未取消)通知数/是否还有。 */ int meow_notify_active_count(void); int meow_notify_count_in_type(meow_notify_category cat); diff --git a/meow_ui_gfx.c b/meow_ui_gfx.c index d6a9798..e7841af 100644 --- a/meow_ui_gfx.c +++ b/meow_ui_gfx.c @@ -67,8 +67,9 @@ static char *g_dump_path = NULL; * g_sbar_collapsed = 目标态(1=上收);g_sbar_collapse_h = 当前实际上收偏移 * (0..MUI_SBAR_H:0=完全展开,MUI_SBAR_H=内容全部上移出屏、仅留提示线)。 * 基座让 meow_ui_statusbar_set_collapsed(on) 立即生效(true→只画提示线/内容上移); - * 过渡(每帧插值)留待后续代理或经 meow_ui_statusbar_animate(ms) 启用的简易线性逼近。 */ -#define MUI_SBAR_HINT_H 6 /* 上收状态下顶部提示线高(px) */ + * 过渡(每帧插值)留待后续代理或经 meow_ui_statusbar_animate(ms) 启用的简易线性逼近。 + * 「收合=只留提示线」占高 = MUI_SBAR_COLLAPSE_H(=MEOW_SBAR_HOTZONE_FS=4,meow_ui_sys.h), + * 与 inset(meow_ui_statusbar_height())/顶部下滑热区/收合渲染同口径——replaced MUI_SBAR_HINT_H。 */ static int g_sbar_collapsed = 0; /* 目标:1=上收 */ static int g_sbar_collapse_h = 0; /* 当前上收偏移(0..MUI_SBAR_H) */ static int g_sbar_collapse_anim_ms = 0; /* 过渡时长(0=瞬切);meow_ui_statusbar_animate 设置 */ @@ -357,7 +358,8 @@ void meow_ui_swipe_hint(int x) if (!g_init_ok) return; const meow_theme *th = meow_theme_get(); /* 进度条画在状态栏下沿(z 在状态栏之下,不被全局状态栏覆盖)。 - * 有状态栏(非全屏)→ y = 状态栏高度;无状态栏(全屏/隐藏)→ y=0。 */ + * 有状态栏(非全屏)→ y = 状态栏高度;收合(collapsed)→ y=提示线下缘 + * (MUI_SBAR_COLLAPSE_H,与提示线不重叠);无状态栏(全屏/隐藏)→ y=0。 */ int hy = meow_ui_statusbar_height(); if (x > 0) { if (x > g_xres) x = g_xres; @@ -544,15 +546,16 @@ static int statusbar_collapse_in_transition(void) /* 上收状态栏:内容整体上移出屏(不画时钟/电量),仅在顶部画一条「提示线」。 * 与 meow_ui_swipe_hint(左滑返回进度条,5px,动态)不同:此线是「状态栏可下拉还原」的 - * 常驻提示(主题强调色下边缘)。原状态栏整块清回主题背景,避免残影(app inset 之内)。 */ + * 常驻提示(主题强调色下边缘)。收合只占/清「提示线」高度区域(MUI_SBAR_COLLAPSE_H=4), + * 不再清整 MUI_SBAR_H(28) 行 —— app 内容按 inset=提示线高可上移到提示线下缘,无 28px 空条。 */ static void statusbar_collapsed_render(int h) { (void)h; const meow_theme *th = meow_theme_get(); int w = meow_ui_width(); - /* h 越大内容越贴顶;基座:折叠态整块清底 + 顶部提示线(提示线下缘=主题 accent) */ - meow_ui_fill_rect(0, 0, w, MUI_SBAR_H, th->bg); - meow_ui_fill_rect(0, MUI_SBAR_HINT_H - 1, w, 1, th->accent); + /* h 越大内容越贴顶;基座:折叠态清「提示线」区域底 + 顶部提示线(提示线下缘=主题 accent) */ + meow_ui_fill_rect(0, 0, w, MUI_SBAR_COLLAPSE_H, th->bg); + meow_ui_fill_rect(0, MUI_SBAR_COLLAPSE_H - 1, w, 1, th->accent); } /* ---- 公共 API(基座实现;动画插值骨架见 statusbar_collapse_step)---- */ @@ -576,9 +579,10 @@ void meow_ui_statusbar_animate(int ms) int meow_ui_statusbar_hotzone(void) { /* 非全屏=整条状态栏可视;全屏时缩小到 MEOW_SBAR_HOTZONE_FS(保留一处下拉入口)。 - * 上收(collapsed、非全屏)时热区=提示线下缘高度。全屏态由 meow_ui_sys 维护。 */ + * 上收(collapsed、非全屏)时热区=提示线下缘高度(MUI_SBAR_COLLAPSE_H,与渲染/inset 同口径)。 + * 全屏态由 meow_ui_sys 维护。 */ if (meow_ui_sys_statusbar_active()) - return g_sbar_collapsed ? MUI_SBAR_HINT_H : MUI_SBAR_H; + return g_sbar_collapsed ? MUI_SBAR_COLLAPSE_H : MUI_SBAR_H; return MEOW_SBAR_HOTZONE_FS; } @@ -602,10 +606,12 @@ static void present(void) if (!g_refresh_always_max) { if (g_last_present_ms && now - g_last_present_ms < (unsigned long long)period) return; } - /* 全局状态栏(A-0)+ 上收(collapsed)基座:上屏前把状态栏画进 back_buf 顶部 MUI_SBAR_H 行。 - * 全屏/隐藏时 active()==0 不画(沉浸),app 内容可占满整屏。触发条件: - * 数据已变(1s 轻刷/显隐切换) / 顶条被 app 内容触及 / 上收动画未到位(逐帧推进)。 - * 展开态仍走 meow_ui_sys_statusbar_render()(meow_ui_sys.c 不改,留给后续代理); + /* 全局状态栏(A-0)+ 上收(collapsed)基座:上屏前把状态栏画进 back_buf 顶部行。 + * 展开态=整条(MUI_SBAR_H 行);收合态只占/清「提示线」区域(MUI_SBAR_COLLAPSE_H 行), + * app 内容(inset=提示线高)可上移到提示线下缘,无 28px 空条。全屏/隐藏时 active()==0 + * 不画(沉浸),app 内容可占满整屏。触发条件:数据已变(1s 轻刷/显隐切换) / + * 顶条被 app 内容触及 / 上收动画未到位(逐帧推进)。展开态仍走 + * meow_ui_sys_statusbar_render()(meow_ui_sys.c 不改,留给后续代理); * 上收折叠态(collapse_h>=MUI_SBAR_H)由基座只画提示线(内容上移出屏)。 */ if (meow_ui_sys_statusbar_active() && (meow_ui_sys_statusbar_force() || statusbar_touched() || diff --git a/meow_ui_input.c b/meow_ui_input.c index 7a155d2..8c7d0b9 100644 --- a/meow_ui_input.c +++ b/meow_ui_input.c @@ -317,8 +317,12 @@ void meow_ui_run(const meow_ui_events *ev) if (in_startup) continue; /* 启动窗口进度条静音 */ int x0 = e.a, xc = e.b, dx = xc - x0; /* dx=当前X-按下X */ /* SDK 自动画顶部条:长度=手指当前 X(=距左缘距离,向内划长度随位移增长)。 - * 全屏/沉浸不画系统条(无返回手势);APP 若注册 on_swipe_progress 仍可自绘。 */ - if (meow_ui_sys_statusbar_active()) meow_ui_swipe_hint(xc); + * M2.5x/A-13:全屏/收合下「左缘返回手势保留」,顶部动态提示条(meow_ui_swipe_hint) + * 仍需绘制——不再以 meow_ui_sys_statusbar_active()(全屏/隐藏=0)开关,否则全屏 + * 时滑条完全不显示。条位 y=meow_ui_statusbar_height():非全屏=状态栏下沿、 + * 全屏=0、收合=提示线下缘(MUI_SBAR_COLLAPSE_H),与提示线不重叠加。 + * APP 若注册 on_swipe_progress 仍可自绘(系统条照常画)。 */ + meow_ui_swipe_hint(xc); if (g_events_set && g_events.on_swipe_progress) g_events.on_swipe_progress(x0, dx); meow_ui_flush_region(0, meow_ui_statusbar_height(), meow_ui_width(), 6); diff --git a/meow_ui_notify.c b/meow_ui_notify.c index dbad728..489ab52 100644 --- a/meow_ui_notify.c +++ b/meow_ui_notify.c @@ -35,7 +35,10 @@ /* ---- 内部活动表尺寸(配额见 meow_ui.h) ---- */ #define MEOW_NOTIFY_MAX_ACTIVE (MEOW_NOTIFY_MAX_TYPES * MEOW_NOTIFY_MAX_PER_TYPE) -/* 活动通知槽(内部;标题/正文等复制副本,不持 app 外部指针,避免栈字符串悬垂) */ +/* 活动槽内 icon 缓冲上限(与 IPC app_icon[48] 一致;SDK 复制副本,不持 app 外部指针) */ +#define MEOW_SLOT_ICON_MAX 48 + +/* 活动通知槽(内部;标题/正文/图标等复制副本,不持 app 外部指针,避免栈字符串悬垂) */ typedef struct { uint32_t id; /* 本地唯一 id(app 侧句柄);0=空闲槽 */ uint32_t ipc_id; /* launcher 中央存储分配的通知 id(nid);0=未同步/降级 */ @@ -47,6 +50,7 @@ typedef struct { char title[64]; char body[128]; char target_activity[32]; + char icon[MEOW_SLOT_ICON_MAX]; /* 图标 name/path;空=用 app 默认图标(launcher 回退) */ int progress; /* 0-100;-1=无进度条 */ } notify_slot_t; @@ -111,6 +115,7 @@ static void notify_fill(notify_slot_t *s, const meow_notify *n, uint32_t id, uin snprintf(s->body, sizeof s->body, "%s", n->body ? n->body : ""); snprintf(s->target_activity, sizeof s->target_activity, "%s", n->target_activity ? n->target_activity : ""); + snprintf(s->icon, sizeof s->icon, "%s", n->icon ? n->icon : ""); s->progress = (n->progress < -1) ? -1 : (n->progress > 100 ? 100 : n->progress); } @@ -253,6 +258,28 @@ int meow_notify_cancel(uint32_t id) return rc; } +int meow_notify_update_progress(uint32_t local_id, int progress) +{ + int p, i, found = 0; + uint32_t ipc_id = 0; + if (local_id == 0) return 0; + p = (progress < -1) ? -1 : (progress > 100 ? 100 : progress); /* 钳位 [-1,100] */ + pthread_mutex_lock(&g_mu); + notify_purge_expired(); + for (i = 0; i < MEOW_NOTIFY_MAX_ACTIVE; i++) { + if (g_slots[i].active && g_slots[i].id == local_id) { + g_slots[i].progress = p; /* 实时:仅改内存活动槽,不落盘 */ + ipc_id = g_slots[i].ipc_id; /* launcher 侧 nid;0=未同步/降级 */ + found = 1; + break; + } + } + pthread_mutex_unlock(&g_mu); + /* 落点(best-effort):经 IPC 通知 launcher 改其内存活跃槽;无 launcher/未同步则静默。 */ + if (found && ipc_id) meow_notify_ipc_update_progress(ipc_id, p); + return found; +} + int meow_notify_active_count(void) { int i, n = 0; diff --git a/test_notify_integration.c b/test_notify_integration.c index 6ea53d9..2f50f0a 100644 --- a/test_notify_integration.c +++ b/test_notify_integration.c @@ -41,7 +41,7 @@ uint64_t meow_ui_time_ms(void) /* ---- stub server 捕获(每次连接一帧) ---- */ #define MAXCAP 16 -typedef struct { int type; m_notify_t n; uint32_t dismiss_id; uint32_t nid; } srec_t; +typedef struct { int type; m_notify_t n; m_notify_progress_t prog; uint32_t dismiss_id; uint32_t nid; } srec_t; static srec_t g_rec[MAXCAP]; static int g_nrec = 0; static int g_stop = 0; @@ -77,6 +77,7 @@ static void *server(void *arg) memset(&g_rec[idx], 0, sizeof g_rec[idx]); g_rec[idx].type = (int)type; if (type == MSG_NOTIFY && len >= sizeof(m_notify_t)) memcpy(&g_rec[idx].n, pay, sizeof(m_notify_t)); + if (type == MSG_NOTIFY_PROGRESS && len >= sizeof(m_notify_progress_t)) memcpy(&g_rec[idx].prog, pay, sizeof(m_notify_progress_t)); if (type == MSG_NOTIFY_DISMISS && len >= 4) memcpy(&g_rec[idx].dismiss_id, pay, 4); /* 分配 nid = 100 + NOTIFY 序号 */ uint32_t seq = 0; @@ -85,7 +86,7 @@ static void *server(void *arg) uint32_t ack_nid = g_rec[idx].nid; pthread_mutex_unlock(&g_mu); - if (type == MSG_NOTIFY || type == MSG_NOTIFY_DISMISS) { + if (type == MSG_NOTIFY || type == MSG_NOTIFY_DISMISS || type == MSG_NOTIFY_PROGRESS) { m_ack_t a; memset(&a, 0, sizeof a); a.code = MEOW_OK; a.status = ack_nid; uint8_t rhdr[MEOW_IPC_HDR_LEN]; uint32_t mag = MEOW_IPC_MAGIC; uint16_t ver = MEOW_IPC_VERSION, t = (uint16_t) @@ -140,11 +141,13 @@ int main(void) /* S1 通用通知 */ meow_notify nt; memset(&nt, 0, sizeof nt); nt.cat = MEOW_NOTIFY_CAT_GENERAL; nt.urgency = MEOW_NOTIFY_URG_NORMAL; - nt.title = "t1"; nt.timeout_ms = 0; + nt.title = "t1"; nt.timeout_ms = 0; nt.icon = "os.meow.hello/icon.png"; uint32_t id1 = meow_notify_send(&nt); check("S1 send 返回本地 id=1", id1 == 1); check("S1 active_count=1", meow_notify_active_count() == 1); check("S1 stub 回 nid=101", rec_at(0).nid == 101); + check("S1 映射 urgency=NORMAL(1)", rec_at(0).n.urgency == MEOW_NOTIFY_URG_NORMAL); + check("S1 映射 icon→app_icon", !strcmp(rec_at(0).n.app_icon, "os.meow.hello/icon.png")); /* S2 媒体通知:单例 + media=1 */ memset(&nt, 0, sizeof nt); @@ -182,6 +185,25 @@ int main(void) 触发丢最旧的是 S5 循环第 4 次 send = 第 7 条 NOTIFY(rec[7]),其 replaces_id==103。 */ check("S5 丢最旧后 send replaces_id=被丢nid(103)", rec_at(7).n.replaces_id == 103); + /* S5b update_progress:新发一条 progress → 本地 id → update → stub 捕获 MSG_NOTIFY_PROGRESS */ + memset(&nt, 0, sizeof nt); + nt.cat = MEOW_NOTIFY_CAT_PROGRESS; nt.title = "prog"; nt.timeout_ms = 0; + nt.progress = 10; + uint32_t idp = meow_notify_send(&nt); + int ni = -1; + for (int i = 0; i < MAXCAP; i++) { + srec_t r = rec_at(i); + if (r.type == MSG_NOTIFY && !strcmp(r.n.summary, "prog")) { ni = i; break; } + } + check("S5b 找到 prog 通知帧", ni >= 0); + int rc_up = meow_notify_update_progress(idp, 70); + check("S5b update_progress 返回 1", rc_up == 1); + srec_t pr = rec_at(ni + 1); + check("S5b stub 捕获 MSG_NOTIFY_PROGRESS 帧", pr.type == MSG_NOTIFY_PROGRESS); + check("S5b progress nid=该通知 launcher nid", ni >= 0 && pr.prog.nid == rec_at(ni).nid); + check("S5b progress 载=70", pr.prog.progress == 70); + check("S5b update_progress(未知 id) 返回 0", meow_notify_update_progress(0xFFFFFF, 50) == 0); + stop_server(); pthread_join(th, NULL); diff --git a/test_notify_ipc.c b/test_notify_ipc.c index f1ba227..c7f5dc3 100644 --- a/test_notify_ipc.c +++ b/test_notify_ipc.c @@ -30,6 +30,7 @@ typedef struct { uint32_t type; /* 收到的帧类型 */ m_notify_t n; /* MSG_NOTIFY 负载 */ + m_notify_progress_t prog; /* MSG_NOTIFY_PROGRESS 负载 */ uint32_t dismiss_id; /* MSG_NOTIFY_DISMISS 负载 */ uint32_t req_id; uint32_t nid_to_return; /* ACK 中回填的 status */ @@ -47,6 +48,7 @@ static void cap_store(uint32_t type, uint32_t req_id, const uint8_t *pay, uint32 g_cap.req_id = req_id; g_cap.nid_to_return = nid_to_return; if (type == MSG_NOTIFY && len >= sizeof(m_notify_t)) memcpy(&g_cap.n, pay, sizeof(m_notify_t)); + if (type == MSG_NOTIFY_PROGRESS && len >= sizeof(m_notify_progress_t)) memcpy(&g_cap.prog, pay, sizeof(m_notify_progress_t)); if (type == MSG_NOTIFY_DISMISS && len >= 4) memcpy(&g_cap.dismiss_id, pay, 4); g_cap.got = 1; pthread_mutex_unlock(&g_cap_mu); @@ -87,7 +89,7 @@ static void *server_thread(void *arg) /* MSG_NOTIFY → 回 m_ack_t(code=OK, status=nid_to_return); * MSG_NOTIFY_DISMISS → 回 m_ack_t(code=OK, status=0,同 launcher 行为)。 */ - if (type == MSG_NOTIFY || type == MSG_NOTIFY_DISMISS) { + if (type == MSG_NOTIFY || type == MSG_NOTIFY_DISMISS || type == MSG_NOTIFY_PROGRESS) { m_ack_t a; memset(&a, 0, sizeof a); a.code = MEOW_OK; @@ -152,9 +154,10 @@ int main(void) meow_notify nt; memset(&nt, 0, sizeof nt); nt.cat = MEOW_NOTIFY_CAT_PROGRESS; - nt.urgency = MEOW_NOTIFY_URG_NORMAL; + nt.urgency = MEOW_NOTIFY_URG_HIGH; nt.title = "下载中"; nt.body = "70%"; + nt.icon = "os.meow.hello/icon.png"; nt.timeout_ms = 5000; /* >0 → expire=5000, ongoing=0 */ nt.progress = 150; /* 钳位 → 100 */ uint32_t nid = meow_notify_ipc_send(&nt, 0); @@ -175,6 +178,8 @@ int main(void) check("T2 replaces_id 透传=0", cn->replaces_id == 0); check("T1 app_id=testapp", !strcmp(cn->app_id, "testapp")); check("T1 summary=下载中", !strcmp(cn->summary, "下载中")); + check("T1 urgency(HIGH)→wire urgency=2", cn->urgency == MEOW_NOTIFY_URG_HIGH); + check("T1 icon→app_icon", !strcmp(cn->app_icon, "os.meow.hello/icon.png")); } /* ---------- T1/T2:媒体类型 + ONGOING(-1) ---------- */ @@ -275,6 +280,21 @@ int main(void) check("T5 dismiss 负载=nid=0xABCD", g_cap.dismiss_id == 0xABCDu); } + /* ---------- T6:update_progress 发 MSG_NOTIFY_PROGRESS(nid + progress 钳位)---------- */ + { + char sock[108]; + g_listen_fd = arb_listen(sock, sizeof sock); + setenv("MEOW_IPC_SOCK", sock, 1); + pthread_t th; + pthread_create(&th, NULL, server_thread, NULL); + meow_notify_ipc_update_progress(0x1234, 250); /* 钳位 250→100 */ + pthread_join(th, NULL); + close(g_listen_fd); + check("T6 stub 收到 MSG_NOTIFY_PROGRESS 帧", g_cap.got && g_cap.type == MSG_NOTIFY_PROGRESS); + check("T6 progress 负载 nid=0x1234", g_cap.prog.nid == 0x1234); + check("T6 progress 钳位 250→100", g_cap.prog.progress == 100); + } + printf("\n%s (%d failure%s)\n", failures ? "RESULT: FAIL" : "RESULT: PASS", failures, failures == 1 ? "" : "s"); return failures ? 1 : 0; diff --git a/test_notify_post.c b/test_notify_post.c index beac070..b3d0440 100644 --- a/test_notify_post.c +++ b/test_notify_post.c @@ -49,6 +49,7 @@ static void launcher_map(const m_notify_t *mn, meow_notif_t *n) 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(静默) */ n->ongoing = mn->ongoing; n->media = mn->media; } @@ -59,6 +60,15 @@ static void launcher_map(const m_notify_t *mn, meow_notif_t *n) static int g_listen_fd = -1; static int g_stop = 0; static uint32_t g_posted_nid = 0; +static meow_notif_t g_stored; /* 回读 launcher 存储的最近一条通知(store_cb) */ +static int g_store_called = 0; + +static int store_cb(const meow_notif_t *n, void *ctx) +{ + (void)ctx; + if (n) { g_stored = *n; g_store_called = 1; } + return 1; /* 只取第一条 */ +} static void reply_ack(int c, uint16_t type, uint32_t req_id, uint32_t status) { @@ -130,13 +140,21 @@ int main(void) /* SDK send → launcher 真存储。media + ONGOING 走全部映射分支。 */ meow_notify nt; memset(&nt, 0, sizeof nt); - nt.cat = MEOW_NOTIFY_CAT_MEDIA; nt.urgency = MEOW_NOTIFY_URG_NORMAL; + nt.cat = MEOW_NOTIFY_CAT_MEDIA; nt.urgency = MEOW_NOTIFY_URG_HIGH; nt.title = "正在播放"; nt.timeout_ms = -1; nt.progress = 40; + nt.icon = "os.meow.hello/icon.png"; uint32_t nid = meow_notify_ipc_send(&nt, 0); check("send 经 launcher 真存储回 nid>0", nid != 0); check("send 拿到 launcher 分配的 nid(与 post 一致)", g_posted_nid == nid); check("launcher 存储 count(media app)=1", meow_notif_count("intgapp") == 1); + /* 回读 launcher 存储,验证 urgency/icon/progress 全链路落库 */ + g_store_called = 0; memset(&g_stored, 0, sizeof g_stored); + meow_notif_list(NULL, store_cb, NULL); + check("post 存储 urgency=HIGH(2)", g_store_called && g_stored.urgency == MEOW_NOTIFY_URG_HIGH); + check("post 存储 icon→app_icon", g_stored.app_icon[0] && !strcmp(g_stored.app_icon, "os.meow.hello/icon.png")); + check("post 存储 progress=40", g_stored.progress == 40); + /* SDK dismiss → launcher 撤下 */ meow_notify_ipc_dismiss(nid); check("dismiss 后 launcher 存储 count=0", meow_notif_count("intgapp") == 0);