- 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 推卡自刷验收全绿
289 lines
14 KiB
C
289 lines
14 KiB
C
/* 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;
|
||
}
|