通知模型补全(agent-107: SDK icon+update_progress / IPC urgency+MSG_NOTIFY_PROGRESS / 存储 urgency+icon回退+progress / CSV加3列) + 全屏收合视觉+滑条(agent-108)

This commit is contained in:
MeowOS Dev committed 2026-08-30 13:02:17 +08:00
1 parent 663b0aa11d
commit a8cd9fe5dc
9 files changed
+176 -29

No files matched your search

+42 -3
View File
@@ -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);
}
+7 -2
View File
@@ -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
+8 -2
View File
@@ -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);
+19 -13
View File
@@ -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() ||
+6 -2
View File
@@ -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);
+28 -1
View File
@@ -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;
+25 -3
View File
@@ -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);
+22 -2
View File
@@ -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;
+19 -1
View File
@@ -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);