通知模型补全(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
@@ -29,13 +29,13 @@
|
||||
```
|
||||
id(uint32) / app_name / app_icon / summary / body / actions[] / hints{}
|
||||
expire_timeout / created_ms / dismissed / ongoing(bool)
|
||||
target_activity(可选, 来自 hints) / progress(可选 int)
|
||||
target_activity(可选, 来自 hints) / progress(可选 int) / urgency(0-3, 缺省 NORMAL)
|
||||
```
|
||||
统一从 IPC/D-Bus 两路解析入该结构。
|
||||
统一从 IPC/D-Bus 两路解析入该结构。`app_icon`(SDK `icon`):空时按 `app_id` 回退该 app 默认图标(launcher 由 app.json `icon` 提供;通知中心 UI 兜底)。
|
||||
|
||||
## 5. 存储分层(用户已定 2026-08-29)
|
||||
- **关键配置优先板载**:设置类(notify.* 配额等)存板载 `/usr/data/meow/settings.conf`(B2a),确保**无卡时系统正常工作**。
|
||||
- **实时通知 → 本地内存**(板载,动态分配/ring)。
|
||||
- **实时通知 → 本地内存**(板载,动态分配/ring)。**progress 实时进度 → 仅改内存活跃槽**(用户定案 2026-08-30);**落盘快照只在创建时/结束(100% 或 dismiss)**,避免每条 live 写卡。
|
||||
- **超时/转储 → TF 卡** `MeowOS/notifications/`(**系统约定:系统文件统一放 MeowOS/ 下**,非 MeowData/——MeowData 是应用专属数据根)(转储区,**appended 日志行 CSV**,见 §8)。
|
||||
- **无 TF 卡可运行**(本地内存态即可,不依赖卡;TF 卡在时才转储)。转储/日志类是可降级数据(无卡用 /tmp 回退),配置类永远板载。
|
||||
- **本地数据清理策略**:
|
||||
@@ -61,6 +61,8 @@ target_activity(可选, 来自 hints) / progress(可选 int)
|
||||
- **异常关机**:开机时清理板载残留通知数据。
|
||||
- TF 卡转储区 `MeowOS/notifications/`:保留**最新 10MB**(ring/截断),**不被电源事件清理**(持久留存)。
|
||||
- **转储格式(定案 #6,M2.5x)**:`MeowOS/notifications/` 写为 **appended 日志行**(每条通知一行,CSV 风格:`id,app_id,分类,标题,时间,…`),**按总大小滚动截断**(保留最新 **10MB**)。与既有「10s 转储」衔接:创建 10s 后即按此格式追加一行 `[源:用户 2026-08-30]`。
|
||||
- **CSV 列(模型补全,2026-08-30)**:`timestamp,id,app_id,app_icon,category,urgency,progress,title,body,target_activity,ongoing,media,dismissed`(`urgency` 0-3、progress 当前值 -1=无;`app_icon`=SDK `icon`,空→app 图标回退)。
|
||||
- **progress 落盘规则(模型补全,用户定案)**:**里程碑落盘**——快照只在**创建时 + 结束(100% 或 dismiss)** 触发;实时进度(`meow_notify_update_progress` → `MSG_NOTIFY_PROGRESS`)**仅改内存活跃槽,不每帧落盘**。
|
||||
- **启动时清板载 + 清内存通知表**:开机(含异常关机后)清理板载残留通知数据,并**清空内存通知表**(在既有「异常关机:开机时清理板载残留」基础上**补充内存表一并清**);正常关机/重启前先转储未上卡通知再清板载(沿用既有规则)`[源:用户 2026-08-30]`。
|
||||
- **无 TF 卡**:系统可运行(内存态通知即可),不依赖卡。
|
||||
|
||||
|
||||
@@ -172,6 +172,12 @@
|
||||
- **每 App 最多 4 类型;每类型活动 ≤4;媒体单例(每 App 仅 1 条,可多 App)**;系统事件抽象为 App(`app_id=system`) 走同一配额 `[NOTIFICATION_SYSTEM §7][meow_ui.h][DEV_NOTES #11]`
|
||||
- 通知创建 **10s 后自动转储到 TF `MeowOS/notifications/`**(保留最新 10MB ring,不被电源事件清);正常关机先转储再清板载;无卡可运行 `[NOTIFICATION_SYSTEM §8]`
|
||||
- **通知 TF 转储(定案 #6,M2.5x)**:转储格式 = **appended 日志行**(每条通知一行,CSV 风格:`id,app_id,分类,标题,时间,…`)+ **按总大小滚动截断**(保留最新 **10MB**)。**启动时清板载通知 + 清内存通知表**(开机清残留/内存表);正常关机/重启前**先转储未上卡通知**再清板载(沿用既有规则)`[源:用户 2026-08-30]`
|
||||
- **通知模型补全(2026-08-30 用户定案,全链路 SDK→IPC→存储→dump)**:
|
||||
- 字段:SDK `meow_notify` / IPC `m_notify_t` / 存储 `meow_notif_t` 全链路补 **`urgency`(0-3,缺省 NORMAL)**、**`icon`(name/path,NULL/空 → 用该 app 默认图标,launcher 按 app_id 回退 app.json 的 icon;每条可独立指定)**;`progress` 已有(0-100,-1=无)。
|
||||
- 实时进度:`meow_notify_update_progress(local_id, progress)`(SDK)→ IPC `MSG_NOTIFY_PROGRESS(0x0024)` → launcher 只改**内存活跃槽**(不落盘)。
|
||||
- **里程碑落盘**:TF 快照只在**创建时 + 结束(100% 或 dismiss)** 触发(沿用 `meow_notif_dump_tf`,progress 写当时值);实时进度**不每帧落盘**。
|
||||
- **CSV 列**(`meow_notif_dump_tf`/`tf_line`):`timestamp,id,app_id,app_icon,category,urgency,progress,title,body,target_activity,ongoing,media,dismissed`。
|
||||
- 说明:旧调用(无 urgency/progress/icon)兼容——urgency 缺省 NORMAL、progress=-1、icon=NULL→app 图标。`[源:用户 2026-08-30]`
|
||||
|
||||
### 5.4 配置版本单点 / 刷新率 / 息屏
|
||||
- /etc/meowos_version 版本单点(见 §1.1)`[DEV_NOTES #4]`
|
||||
|
||||
@@ -94,13 +94,14 @@ static void notif_send(int which)
|
||||
n.body = "常驻不自动过期(timeout=-1),需手动取消";
|
||||
n.timeout_ms = MEOW_NOTIFY_TIMEOUT_ONGOING;
|
||||
break;
|
||||
case 5: /* 带 target_activity(SDK 结构暂无 icon 字段,故只填跳转目标) */
|
||||
case 5: /* 带 target_activity + icon(SDK 结构含 icon 字段;NULL/空=用 app 默认图标) */
|
||||
label = "TGT";
|
||||
n.cat = MEOW_NOTIFY_CAT_MESSAGE;
|
||||
n.title = "测试-跳转Activity";
|
||||
n.body = "点击跳转 target_activity=main";
|
||||
n.timeout_ms = 6000;
|
||||
n.target_activity = "main";
|
||||
n.icon = NULL; /* 空 → launcher 用 app 默认图标(app.json icon) */
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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() ||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in new issue
Block a user