Files
AskaEth 3d70a3dd2c feat: M6.10–M6.19 批量收编 + M6.19 推卡验收
- M7 launcher 重写 5 期(虚拟化宫格/meow_theme 单一权威/横幅动画/自定义 PIN 键盘)
- OOM 通用化(cachegov 上收 SDK)、自动切歌根治、meowpanel jv_free 泄漏修复、Hello 滚动卡顿修复
- ncm 歌词 UI / 媒体通知封面 / 封面分辨率 640
- 5 bug 修复:ncm 后台化 UI 叠层+点击穿透、再进 ncm 桌面叠层、播放页新旧 UI 叠层抢刷脏区(meow_ui_gfx 恢复边沿整帧重画)、PIN 亮屏亮度跳(keys.c meow_panel_on 按 settings_brightness_clamped 恢复)、通知中心封面显歌单封面(cover.c save_cover_raw 加 is_song + 命中块守卫)
- 四 App 统一重链新 meow_ui.a 51caee4a;M6.19=0.8.19 固件 2d2331f2 推卡自刷验收全绿
2026-09-03 12:16:01 +08:00

289 lines
14 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* test_notif_center.c — M7.3 通知中心内层迁 SDK 控件(宿主验证)。
*
* 复现 notif_center.c M7.3 SDK 路径所建控件集(meow_listview 虚拟化 vsource + set_item_draw 行自绘 +
* set_empty_text 空态 + set_swipe_delete 左滑删除 + 双 meow_slider + meow_switch + 自定义画布 chip),验证:
* P1 listview 渲染通知行:行卡非背景像素(图标/标题/正文)+ set_item_draw 命中(含滚动换算);
* P2 空态:count==0 且 set_empty_text 设了 → 视口内居中画文案(非背景像素);
* P3 左滑删除:begin/move(超阈值)/end → on_delete 回调恰一次 + swipe 状态复位(撤销回调不触发);
* P4 滑条/开关:meow_slider_set/get 与 meow_switch_set/get 往返一致(值写回控件);
* P5 自定义画布 chip:meow_widget_rect 取几何 + render 出非背景像素 + meow_widget_hit 命中(owner 闸门开)。
*
* 几何/控件创建与 notif_center.c「M7.3 SDK 路径」镜像(owner 钉 NCP_WIDGET_ACT;行高 NCP_ROW_H)。
* 注:宿主不含 launcher sb_icons / s_ui_theme / g_rows,故行/芯片用 meow_ui_text + 简化绘制复现契约;
* notif 行自绘函数体(ncp_lv_row_draw)依赖 launcher 全局,真机由 notif_center.c 提供。
*
* 编译(在 patch_v2/meow_launcherd 下,宿主 gcc):
* gcc -Wall -Wextra -o test_notif_center test_notif_center.c \
* ../sdk/test_host_stub.c ../sdk/meow_ui_gfx.c ../sdk/meow_ui_sys.c \
* ../sdk/meow_ui_theme.c ../sdk/meow_ui_vol.c ../sdk/meow_ui_log.c \
* ../sdk/meow_ui_widget.c ../sdk/meow_ui_input.c ../sdk/meow_ui_activity.c \
* ../sdk/meow_ui_dialog.c ../sdk/meow_ui_ime.c ../sdk/meow_ui_str.c ../sdk/meow_pinyin.c \
* ../sdk/meow_ui_draw.c ../sdk/meow_ui_view.c ../sdk/meow_ui_box.c ../sdk/meow_ui_anim.c \
* ../sdk/meow_ui_scroll.c ../sdk/meow_ui_img.c ../sdk/meow_ui_media.c \
* ../sdk/meow_notify_ipc.c ../sdk/stb_image.c -I../sdk -DMEOW_UI_HOST_TEST -pthread -lm
* 运行:./test_notif_center
*/
#include "meow_ui.h"
#include "meow_ui_internal.h" /* meow_ui_back_buf / meow_gfx_mark_dirty / meow_widget_rect */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
static int g_pass = 0, g_fail = 0;
#define CHECK(cond, ...) do { \
if (cond) { g_pass++; } \
else { g_fail++; printf("FAIL %s:%d ", __FILE__, __LINE__); printf(__VA_ARGS__); printf("\n"); } \
} while (0)
/* ---- 与 notif_center.c / meow_launcher_internal.h 同源几何(镜像) ---- */
#define XRES 480
#define YRES 720
#define NCP_TOP 28
#define NCP_CHIP_Y 86
#define NCP_CHIP_H 44
#define NCP_CHIP_W 70
#define NCP_CHIP_GAP 8
#define NCP_CHIP_N 5
#define NCP_SLIDER_X 20
#define NCP_SLIDER_W (XRES - 40)
#define NCP_SL1_Y 162
#define NCP_SL2_Y 214
#define NCP_DND_Y 264
#define NCP_DND_H 40
#define NCP_DND_SPLIT_X (XRES / 2)
#define NCP_LIST_TOP 308
#define NCP_LIST_BOT 694
#define NCP_ROW_H 92
#define NCP_ROW_X 12
#define NCP_ROW_W (XRES - 24)
#define NCP_ICON_X 26
#define NCP_TEXT_X 72
#define NCP_TEXT_W (NCP_ROW_W - (NCP_TEXT_X - NCP_ROW_X) - 10)
#define NCP_WIDGET_ACT 0x54
#define NCP_MAX_ROWS 8
/* 通知行镜像(宿主自持,替代 launcher g_rows[]/g_ncp_apps)。 */
typedef struct { const char *summary; const char *body; int progress; } trow_t;
static trow_t g_rows[NCP_MAX_ROWS];
static int g_nrows = 0;
/* ---- 像素探测 ---- */
static int px565(int x, int y, uint32_t bg)
{
unsigned short bg565 = ((bg >> 8) & 0xF800) | ((bg >> 5) & 0x07E0) | ((bg >> 3) & 0x001F);
unsigned short p;
unsigned char *bb = meow_ui_back_buf();
int line = meow_ui_back_buf_pitch(), bpp = meow_ui_back_buf_bpp();
if (!bb || x < 0 || y < 0) { p = 0; return 0; }
if (bpp == 16) p = *(unsigned short *)(bb + (size_t)y * line + (size_t)x * 2);
else p = (unsigned short)(*(unsigned int *)(bb + (size_t)y * line + (size_t)x * 4) >> 16);
return (p != bg565) ? 1 : 0;
}
static int region_nonbg(int x, int y, int w, int h, uint32_t bg)
{
int cnt = 0;
for (int dy = 0; dy < h; dy++)
for (int dx = 0; dx < w; dx++)
if (px565(x + dx, y + dy, bg)) cnt++;
return cnt;
}
/* ---- listview 虚拟化源(镜像 ncp_lv_count / ncp_lv_bind) ---- */
static int lv_count(void *ctx) { (void)ctx; return g_nrows; }
static void lv_bind(int idx, meow_lv_item *it, void *ctx)
{
(void)ctx;
memset(it, 0, sizeof *it);
it->enabled = (idx >= 0 && idx < g_nrows) ? 1 : 0;
}
/* 行自绘(镜像 ncp_lv_row_draw 的 SDK 文本渲染;宿主无 launcher 字体/sb_icons,用 meow_ui_text 近似)。 */
static void lv_draw(int idx, int x, int y, int w, int h, int state, void *ctx)
{
(void)w; (void)state; (void)ctx;
if (idx < 0 || idx >= g_nrows) return;
const meow_theme *th = meow_theme_get();
meow_gfx_fill_round(x + NCP_ROW_X, y, NCP_ROW_W, h - 6, 12, th->surfaceContainer);
/* 图标位(无图回退:primary 圆点) */
int icx = x + NCP_ICON_X, icy = y + (h - 32) / 2;
meow_gfx_fill_round(icx + 16, icy + 16, 16, 16, 8, th->primary);
/* app 名 + 标题 + 正文 */
meow_ui_text(x + NCP_TEXT_X, y + 10, "yao", th->font_body, th->onSurfaceVariant);
meow_ui_text(x + NCP_TEXT_X, y + 30, g_rows[idx].summary, th->font_body, th->onSurface);
meow_ui_text(x + NCP_TEXT_X, y + 56, g_rows[idx].body, th->font_body, th->onSurfaceVariant);
if (g_rows[idx].progress >= 0) {
int px = x + NCP_TEXT_X, pw = NCP_TEXT_W, py = y + h - 14;
meow_ui_fill_rect(px, py, pw, 4, th->surfaceVariant);
meow_ui_fill_rect(px, py, g_rows[idx].progress * pw / 100, 4, th->primary);
}
}
/* ---- 左滑删除回调计数 ---- */
static int del_count = 0; static int del_idx = -1;
static void lv_on_delete(int idx, void *ctx)
{ (void)ctx; del_count++; del_idx = idx; }
static meow_widget *make_list(void)
{
meow_widget *lv = meow_listview_create(0, NCP_LIST_TOP, XRES, NCP_LIST_BOT - NCP_LIST_TOP,
0, NULL, NULL, MEOW_LIST_SEL_NONE);
meow_listview_set_vsource(lv, lv_count, lv_bind, NULL);
meow_listview_set_item_draw(lv, lv_draw, NULL);
meow_listview_set_item_h(lv, NCP_ROW_H);
meow_listview_set_empty_text(lv, "暂无通知");
meow_listview_set_swipe_delete(lv, 1, lv_on_delete, NULL);
return lv;
}
/* 推进 meow_anim 直到 swipe 动画结束(模拟 on_tick 节拍)。 */
static void pump_swipe(meow_widget *lv)
{
int guard = 0;
while (meow_listview_swipe_active(lv) && guard++ < 80) {
meow_anim_tick();
usleep(10000);
}
meow_anim_tick();
}
/* 自定义画布 chip(镜像 ncp_chip_render:圆角 + 文本;用 meow_widget_rect 取几何)。 */
static void chip_render(meow_widget *w, const meow_theme *th, void *ctx)
{
(void)ctx;
int rc[4]; meow_widget_rect(w, rc);
meow_gfx_fill_round(rc[0], rc[1], rc[2], rc[3], 12, th->surfaceContainer);
meow_ui_text_center(rc[0] + rc[2] / 2, rc[1] + rc[3] / 2, "chip", th->font_body, th->onSurfaceVariant);
}
int main(void)
{
meow_ui_opts o = { XRES, YRES };
if (meow_ui_init(&o) != 0) { printf("init failed\n"); return 1; }
const meow_theme *th = meow_theme_get();
/* 通知行数据(镜像:多行 + 进度)。8 行(736px) > 视口(386px) → 可滚动。 */
g_nrows = 8;
static const char *summ[8] = {"下载完成","媒体进行中","系统通知","新消息","更新提醒","日程","备份完成","低电量"};
static const char *body[8] = {"文件已就绪","正在播放","低电量提醒","收到 2 条","已就绪","10 分钟后","已成功","请充电"};
for (int i = 0; i < 8; i++) { g_rows[i].summary = summ[i]; g_rows[i].body = body[i]; g_rows[i].progress = (i < 2) ? (100 - i * 60) : -1; }
/* ============ P1 listview 渲染通知行 + 行命中 ============ */
printf("P1 notif listview render + hit\n");
{
meow_ui_activity_push(NCP_WIDGET_ACT);
meow_widget *lv = make_list();
meow_ui_fill_rect(0, 0, XRES, YRES, th->bg);
meow_ui_fill_rect(0, NCP_LIST_TOP, XRES, NCP_LIST_BOT - NCP_LIST_TOP, th->surfaceVariant);
meow_widget_draw(lv);
/* 行 0 卡底(surfaceContainer 覆盖表面 surfaceVariant),行带内非背景像素 */
int row0y = NCP_LIST_TOP + 8;
int rc = region_nonbg(NCP_ROW_X, row0y, NCP_ROW_W - 8, NCP_ROW_H - 16, th->surfaceVariant);
CHECK(rc > 40, "notif row renders (icon/title/body) cnt=%d", rc);
/* 行命中(含滚动换算):点行 1 中心 → idx1 */
CHECK(meow_listview_hit(lv, 100, NCP_LIST_TOP + NCP_ROW_H + NCP_ROW_H / 2) == 1,
"row1 center hits idx1 got %d", meow_listview_hit(lv, 100, NCP_LIST_TOP + NCP_ROW_H + NCP_ROW_H / 2));
/* 列表区外 → -1 */
CHECK(meow_listview_hit(lv, 100, NCP_LIST_TOP - 4) == -1, "above list -> -1");
/* 滚动后命中换算:set_scroll=92 时点行 0 中心 → 视口第一行是 idx1 */
meow_listview_set_scroll(lv, NCP_ROW_H);
CHECK(meow_listview_hit(lv, 100, NCP_LIST_TOP + NCP_ROW_H / 2) == 1,
"scrolled +92px: first visible center hits idx1 got %d",
meow_listview_hit(lv, 100, NCP_LIST_TOP + NCP_ROW_H / 2));
CHECK(meow_listview_max_scroll(lv) > 0, "max_scroll>0 (3 rows overflow? got %d)",
meow_listview_max_scroll(lv));
meow_widget_free(lv);
meow_ui_activity_pop();
}
/* ============ P2 空态(set_empty_text,count==0 走空态路径) ============ */
printf("P2 empty state\n");
{
meow_ui_activity_push(NCP_WIDGET_ACT);
g_nrows = 0;
meow_widget *lv = make_list();
meow_ui_fill_rect(0, NCP_LIST_TOP, XRES, NCP_LIST_BOT - NCP_LIST_TOP, th->surfaceVariant);
meow_widget_draw(lv);
/* count==0 → 不画任何行卡(空态路径:视口仍 surfaceVariant 底,无 surfaceContainer 行卡)。
* 宿主无 CJK 字体,「暂无通知」文案可能不显形,故以「无行卡」为健壮判据(空态路径已走)。 */
int rowc = region_nonbg(NCP_ROW_X, NCP_LIST_TOP + 8, NCP_ROW_W - 8, NCP_ROW_H - 16, th->surfaceVariant);
CHECK(rowc == 0, "empty: no row cards rendered cnt=%d", rowc);
meow_widget_free(lv);
g_nrows = 8; /* 还原 */
meow_ui_activity_pop();
}
/* ============ P3 左滑删除(SDK swipe-delete 回调) ============ */
printf("P3 notif row swipe delete\n");
{
meow_ui_activity_push(NCP_WIDGET_ACT);
del_count = 0; del_idx = -1;
meow_widget *lv = make_list();
int row0y = NCP_LIST_TOP + NCP_ROW_H / 2;
CHECK(meow_listview_swipe_begin(lv, 100, row0y) == 1, "P3 begin on row0 = 1");
/* 左滑超阈值(NCP_ROW_W*2/5 ≈ 182) */
CHECK(meow_listview_swipe_move(lv, 100 - 260, row0y) == 1, "P3 move big left = 1");
CHECK(meow_listview_swipe_end(lv, 100 - 260, row0y) == 1, "P3 end = 1 (delete anim)");
CHECK(meow_listview_swipe_active(lv) == 1, "P3 delete anim active");
pump_swipe(lv);
CHECK(del_count == 1, "P3 on_delete fired once got %d", del_count);
CHECK(del_idx == 0, "P3 on_delete idx0 got %d", del_idx);
CHECK(meow_listview_swipe_active(lv) == 0, "P3 state reset");
CHECK(meow_listview_swipe_offset(lv) == 0, "P3 dx reset 0 got %d", meow_listview_swipe_offset(lv));
/* 撤销:未超阈值 → 回弹,不触发 on_delete */
del_count = 0;
CHECK(meow_listview_swipe_begin(lv, 100, row0y) == 1, "P3 undo begin = 1");
CHECK(meow_listview_swipe_move(lv, 70, row0y) == 1, "P3 undo move left 30px = 1");
CHECK(meow_listview_swipe_end(lv, 70, row0y) == 1, "P3 undo end = 1 (bounce)");
pump_swipe(lv);
CHECK(del_count == 0, "P3 undo: on_delete NOT fired got %d", del_count);
CHECK(meow_listview_swipe_offset(lv) == 0, "P3 undo dx=0 got %d", meow_listview_swipe_offset(lv));
meow_widget_free(lv);
meow_ui_activity_pop();
}
/* ============ P4 滑条 / 开关 set/get ============ */
printf("P4 slider/switch roundtrip\n");
{
meow_ui_activity_push(NCP_WIDGET_ACT);
meow_widget *sl = meow_slider_create(NCP_SLIDER_X, NCP_SL1_Y - 14, NCP_SLIDER_W, 1, 100, 50);
CHECK(meow_slider_get(sl) == 50, "P4 slider init got %d", meow_slider_get(sl));
meow_slider_set(sl, 78);
CHECK(meow_slider_get(sl) == 78, "P4 slider set 78 got %d", meow_slider_get(sl));
meow_slider_set(sl, 5);
CHECK(meow_slider_get(sl) == 5, "P4 slider clamp low 5 got %d", meow_slider_get(sl));
meow_widget *sw = meow_switch_create(0, 0, 0);
CHECK(meow_switch_get(sw) == 0, "P4 switch init off got %d", meow_switch_get(sw));
meow_switch_set(sw, 1);
CHECK(meow_switch_get(sw) == 1, "P4 switch on got %d", meow_switch_get(sw));
CHECK(meow_widget_selected(sw) == 1, "P4 switch on -> selected bit");
meow_widget_free(sl); meow_widget_free(sw);
meow_ui_activity_pop();
}
/* ============ P5 自定义画布 chip:几何 + 渲染 + 命中 ============ */
printf("P5 custom chip widget\n");
{
meow_ui_activity_push(NCP_WIDGET_ACT);
meow_widget *chip = meow_widget_custom_create(NCP_CHIP_Y, NCP_CHIP_Y, NCP_CHIP_W, NCP_CHIP_H,
chip_render, NULL, NULL);
int rc[4]; meow_widget_rect(chip, rc);
CHECK(rc[0] == NCP_CHIP_Y && rc[1] == NCP_CHIP_Y && rc[2] == NCP_CHIP_W && rc[3] == NCP_CHIP_H,
"P5 chip rect x=%d y=%d w=%d h=%d", rc[0], rc[1], rc[2], rc[3]);
meow_ui_fill_rect(0, 0, XRES, YRES, th->bg);
meow_widget_draw(chip);
int cc = region_nonbg(rc[0] + 4, rc[1] + 4, rc[2] - 8, rc[3] - 8, th->bg);
CHECK(cc > 20, "P5 chip renders non-bg cnt=%d", cc);
/* meow_widget_hit(owner 闸门已开):中心命中、区外未命中 */
int cx = rc[0] + rc[2] / 2, cy = rc[1] + rc[3] / 2;
CHECK(meow_widget_hit(chip, cx, cy) == 1, "P5 chip center hit");
CHECK(meow_widget_hit(chip, cx + NCP_CHIP_W + 30, cy) == 0, "P5 chip outside miss");
meow_widget_free(chip);
meow_ui_activity_pop();
}
meow_ui_shutdown();
printf("notif_center: %d pass / %d fail\n", g_pass, g_fail);
return g_fail ? 1 : 0;
}