通知中心 ⑧⑨: 一键清除(保留活动通知) + 面板跟手下拉半程可回(agent-133)

- ⑧ 一键清除: mnp_clearable(ongoing/media/progress) 红线保留活动通知; 先 meow_notif 转储(notifications.log+10MB滚动) 再清空显示; 勿扰左半+清除右半(NCP_DND_SPLIT_X=fb_xres/2)对称, 命中互斥
- ⑨ 面板跟手下拉/半程可回: reveal 连续映射 1:1 跟手(mnp_drag_progress/reveal), 松手 mnp_drag_anchor(fix,40%)=1→补到1024/0→回0收回; 与⑦行左滑/列表滚动/meow_edge_back 互斥; 复用快照+局部刷(panel帧/新露出条)
- meow_ui.a 零改动(d249c6e6); launcher=b140e9e5; 宿主189项ok; 新增0warn
- NOTIFICATION_SYSTEM §9 增 ⑧⑨; MEOW_APP_SDK §5.7 收合动画落点标注
This commit is contained in:
MeowOS committed 2026-08-30 17:45:15 +08:00
1 parent 82b77d64b4
commit dafe31784d
6 files changed
+406 -22

No files matched your search

+132
View File
@@ -311,6 +311,136 @@ static void test_dnd_swipe_pure(void)
CHECK(mnp_swipe_delete(off, row_w, 40, 110) == 0, "mini: -100 back under threshold -> cancel");
}
/* P6 ⑧ 一键清除判定 + ⑨ 面板跟手下拉/半程可回(纯逻辑,header-only):
* - mnp_clearable:红线(ongoing / media / progress>=0 进行中)一律保留;非活动普通通知可清。
* - mnp_drag_progress:reveal→progress(0/fix/线性/越界 clamp)。
* - mnp_drag_reveal:seed+delta 正向/反向/边界 clamp(连续、半程可回)。
* - mnp_drag_anchor:松手锚定(≥40%→前去 fix;<40%→回 0)。
* 另加 mini 状态机:开(跟手下拉→松手过阈值展开/未过收回)+ 关(上滑跟手→松手未过阈值回弹重展/过阈值收回)。 */
static void test_clear_drag_pure(void)
{
fprintf(stderr, "--- P6 clear predicate + panel drag-follow ---\n");
enum { FIX = 1024, PH = 692 };
/* mnp_clearable */
CHECK(mnp_clearable(1, 0, -1) == 0, "clearable: ongoing -> keep");
CHECK(mnp_clearable(0, 1, -1) == 0, "clearable: media -> keep");
CHECK(mnp_clearable(0, 0, 50) == 0, "clearable: progress in-progress(50) -> keep");
CHECK(mnp_clearable(0, 0, 0) == 0, "clearable: progress bar(0) -> keep");
CHECK(mnp_clearable(0, 0, -1) == 1, "clearable: plain inactive -> clear");
CHECK(mnp_clearable(0, 1, 50) == 0, "clearable: media+progress -> keep");
CHECK(mnp_clearable(1, 0, 50) == 0, "clearable: ongoing+progress -> keep");
/* mnp_drag_progress(reveal∈[0,ph] → p∈[0,fix]) */
CHECK(mnp_drag_progress(0, FIX, PH) == 0, "drag_progress reveal=0 -> 0");
CHECK(mnp_drag_progress(PH, FIX, PH) == FIX, "drag_progress reveal=ph -> fix");
CHECK(mnp_drag_progress(PH / 2, FIX, PH) == FIX / 2, "drag_progress mid -> half");
CHECK(mnp_drag_progress(-50, FIX, PH) == 0, "drag_progress negative reveal -> 0");
CHECK(mnp_drag_progress(PH + 50, FIX, PH) == FIX, "drag_progress reveal>ph -> fix");
CHECK(mnp_drag_progress(173, FIX, 692) == FIX * 173 / 692, "drag_progress linear 173/692");
/* mnp_drag_reveal(seed+delta → [0,ph],连续/反向/边界) */
CHECK(mnp_drag_reveal(0, 0, PH) == 0, "drag_reveal seed0 delta0 -> 0");
CHECK(mnp_drag_reveal(0, 300, PH) == 300, "drag_reveal open: seed0+300 -> 300 (1:1)");
CHECK(mnp_drag_reveal(PH, -300, PH) == PH - 300, "drag_reveal close: seed ph -300");
CHECK(mnp_drag_reveal(100, -200, PH) == 0, "drag_reveal clamps below 0 (reverse past top)");
CHECK(mnp_drag_reveal(100, 1000, PH) == PH, "drag_reveal clamps above ph (drag past bottom)");
CHECK(mnp_drag_reveal(0, -50, PH) == 0, "drag_reveal reverse on hidden stays 0");
CHECK(mnp_drag_reveal(50, 0, 0) == 0, "drag_reveal ph<=0 -> 0");
/* mnp_drag_anchor(≥fix*40/100=409 → 1;<409 → 0) */
CHECK(mnp_drag_anchor(409, FIX, 40) == 1, "drag_anchor at 40% (409) -> go open");
CHECK(mnp_drag_anchor(410, FIX, 40) == 1, "drag_anchor above 40% -> go open");
CHECK(mnp_drag_anchor(1024, FIX, 40) == 1, "drag_anchor full -> go open");
CHECK(mnp_drag_anchor(408, FIX, 40) == 0, "drag_anchor below 40% -> collapse");
CHECK(mnp_drag_anchor(0, FIX, 40) == 0, "drag_anchor 0 -> collapse");
CHECK(mnp_drag_anchor(0, FIX, 0) == 1, "drag_anchor thr_pct=0 -> always open");
/* mini 状态机:跟手下拉打开(seed 从0 升)→ 松手锚定 */
int seed = 0, p = 0;
int dy = 0;
/* 手指下移 300px:reveal=seed+(tly-drag_y0) */
seed = 0; p = mnp_drag_progress(mnp_drag_reveal(seed, 300, PH), FIX, PH);
CHECK(p > 0 && p < FIX, "mini open-follow: drag 300px -> partial progress");
CHECK(mnp_drag_anchor(p, FIX, 40) == 1, "mini open-follow: 300/692=43% >= 40% -> open");
/* 反向拖回 200(reveal=200,即拖 200)→ 半程取消(29% < 40%) */
p = mnp_drag_progress(mnp_drag_reveal(seed, 200, PH), FIX, PH);
CHECK(p < FIX && mnp_drag_anchor(p, FIX, 40) == 0, "mini open-follow: 200/692=29% < 40% -> collapse(cancel)");
/* 全拉满 → 满 */
CHECK(mnp_drag_anchor(mnp_drag_progress(mnp_drag_reveal(seed, PH, PH), FIX, PH), FIX, 40) == 1,
"mini open-follow: drag full -> open");
/* 关闭:seed=PH 上滑 600 → reveal=PH-600 → 过阈值收回 */
int close_p = mnp_drag_progress(mnp_drag_reveal(PH, -600, PH), FIX, PH);
CHECK(close_p < FIX && mnp_drag_anchor(close_p, FIX, 40) == 0, "mini close-follow: up 600 -> close");
/* 关闭:上滑仅 150(reveal=PH-150,仍高)→ 松手回弹重展 */
int reopen_p = mnp_drag_progress(mnp_drag_reveal(PH, -150, PH), FIX, PH);
CHECK(reopen_p > 0 && mnp_drag_anchor(reopen_p, FIX, 40) == 1, "mini close-follow: up 150 -> reopen");
}
/* P7 ⑧ 一键清除(存储契约):先用 mnp_clearable + meow_notif_list/dismiss 模拟面板 ncp_clear_all,
* 验证「先转储留档(dismiss 触发 dump_cb)→ 清出显示」且红线项(ongoing/media/progress)保留。 */
typedef struct { uint32_t ids[8]; int n; } clear_ctx_t;
static int clear_collect_cb(const meow_notif_t *n, void *ctx)
{
clear_ctx_t *c = ctx;
if (!mnp_clearable(n->ongoing, n->media, n->progress)) return 0;
if (c->n >= 8) return 1;
c->ids[c->n++] = n->id;
return 0;
}
/* 通用「收集全部活跃 id」(兼作测试排空用:非仅可清)。 */
static int clear_all_cb(const meow_notif_t *n, void *ctx)
{
clear_ctx_t *c = ctx;
if (c->n >= 8) return 1;
c->ids[c->n++] = n->id;
return 0;
}
static void test_clear_storage(void)
{
fprintf(stderr, "--- P7 clear-all storage contract (dump then clear, keep ongo/media/progress) ---\n");
/* 排空上一测试留下的所有活跃通知,保证本测试从干净基数开始(count 断言可用绝对值)。 */
for (int pass = 0; pass < 8 && meow_notif_count(NULL) > 0; pass++) {
clear_ctx_t c2; memset(&c2, 0, sizeof c2);
meow_notif_list(NULL, clear_all_cb, &c2); /* clear_all_cb:收集全部(非仅可清) */
if (c2.n == 0) break;
for (int i = 0; i < c2.n; i++) meow_notif_dismiss(c2.ids[i]);
}
meow_notif_t n; memset(&n, 0, sizeof n);
/* 普通(可清) */
snprintf(n.app_id, sizeof n.app_id, "os.meow.app"); snprintf(n.category, sizeof n.category, "general");
snprintf(n.summary, sizeof n.summary, "普通1"); n.progress = -1; n.urgency = 1;
uint32_t idA = 0; CHECK(meow_notif_post(&n, &idA) == NOTIF_OK, "clear st: post plain1");
/* 普通2(可清) */
memset(&n, 0, sizeof n);
snprintf(n.app_id, sizeof n.app_id, "os.meow.app"); snprintf(n.category, sizeof n.category, "general");
snprintf(n.summary, sizeof n.summary, "普通2"); n.progress = -1; n.urgency = 1;
uint32_t idB = 0; CHECK(meow_notif_post(&n, &idB) == NOTIF_OK, "clear st: post plain2");
/* 媒体(红线:保留) */
memset(&n, 0, sizeof n);
snprintf(n.app_id, sizeof n.app_id, "os.meow.app"); snprintf(n.category, sizeof n.category, "media");
snprintf(n.summary, sizeof n.summary, "媒体"); n.ongoing = 1; n.media = 1; n.progress = 30; n.urgency = 2;
uint32_t idM = 0; CHECK(meow_notif_post(&n, &idM) == NOTIF_OK, "clear st: post media");
/* 进度进行中(红线:保留) */
memset(&n, 0, sizeof n);
snprintf(n.app_id, sizeof n.app_id, "os.meow.app"); snprintf(n.category, sizeof n.category, "general");
snprintf(n.summary, sizeof n.summary, "下载中"); n.progress = 60; n.urgency = 2;
uint32_t idP = 0; CHECK(meow_notif_post(&n, &idP) == NOTIF_OK, "clear st: post progress");
CHECK(meow_notif_count(NULL) == 4, "clear st: count 4 before clear");
/* 按 mnp_clearable 收集 + dismiss(= dump_cb + 释放槽,模拟 ncp_clear_all) */
clear_ctx_t c; memset(&c, 0, sizeof c);
meow_notif_list(NULL, clear_collect_cb, &c);
int cleared = 0;
for (int i = 0; i < c.n; i++) { meow_notif_dismiss(c.ids[i]); cleared++; }
CHECK(cleared == 2, "clear st: exactly 2 plain cleared");
CHECK(meow_notif_count(NULL) == 2, "clear st: 2 remaining after clear (media+progress kept)");
grab_list();
CHECK(find_slot("媒体") != NULL, "clear st: media kept");
CHECK(find_slot("下载中") != NULL, "clear st: progress kept");
CHECK(find_slot("普通1") == NULL && find_slot("普通2") == NULL, "clear st: plain dismissed");
}
static void test_storage_contract(void)
{
fprintf(stderr, "--- P0 storage contract (panel reads) ---\n");
@@ -415,7 +545,9 @@ int main(void)
test_scroll_pure();
test_anim_pure();
test_dnd_swipe_pure();
test_clear_drag_pure();
test_storage_contract();
test_clear_storage();
unlink(g_tflog); rmdir(dir);