Files
MeowOS-For-R3P2/patch_v2/ncm_app/app/ui/chrome.c
T
MeowOS bdd9e898f4 feat(ncm §20): 音质体系全量——八档UI/VIP检测缓存/逐级降级下首恢复/fee门禁/默认极高HQ
- core: 9档枚举(顺序=降级链, DOLBY映射保留不进UI/链) + level_key(sky/sky_c51)
  + level_gate(CVIP/SVIP门槛) + song_url surround档附加immerseType=c51
  + song_url_auto全档表降级(out_level回填/响应level静默降档采信/try_fn可注入)
  + vip_info(weapi v2优先v1兜底) + parse_vip_info离线可测 + NCM_ERR_VIP(-15)
- app/logic/account.c(新): account.json会员态缓存(uid/nick/vip/双expire/checked_at)
  刷新=登录后/超1天/到期后各一次不轮询; account_vip本地到期降档视图
- player_manager: 用户档按会员钳顶后逐级降级(不硬拒,缓存键仍用户档),
  fee∈{1,4}且非会员→NCM_ERR_VIP零请求(缓存命中优先), eff_level回填
- UI: 设置页八档(规格+门槛+未解锁标注,允许选定), 播放页时间行中央
  生效档/已降级至xx小字, VIP专享文案; 默认档Hi-Res→极高HQ(存量设置保留)
- 验证(0真实请求): degrade_sim 90例矩阵全PASS; player_flow_sim P1-P8全PASS;
  test_ncm_core vip解析9例宿主+qemu全PASS; 窗口/分页/缓存/封面/audiod sim全PASS;
  mipsel -Wall -Wextra 0 warning; 6页离屏rc=0。未部署真机(留主会话)
2026-09-01 15:55:42 +08:00

140 lines
5.3 KiB
C
Raw 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.
/* ui/common/chrome.c — dev.player.ncm 共享 chrome(TopAppBar 建置 / 几何 / 空态&页脚 / 底部提示条)。
*
* 与 files_app/ui/common/chrome.c 同理:把「页面 chrome 固定不随内容重建」分出去。绘制全部读
* meow_theme_get() 语义色(§8.1),不硬编码配色。topbar 由 meow_topappbar 建,自动注册进绘制物
* 注册表(§6.2),owner=创建时 active activity。
*/
#include "../ncm_app_internal.h"
#include <string.h>
int ncm_statusbar(void)
{
int sb = meow_ui_statusbar_height();
return sb < 0 ? 0 : sb;
}
ncm_rect_t ncm_topbar_rect(void)
{
int sb = ncm_statusbar();
return NR(0, sb, meow_ui_width(), APP_TOPBAR_H);
}
ncm_rect_t ncm_content_rect(void)
{
int sb = ncm_statusbar();
int y = sb + APP_TOPBAR_H;
int h = meow_ui_height() - y;
if (h < 0) h = 0;
return NR(0, y, meow_ui_width(), h);
}
meow_widget *ncm_topbar_create(const char *title, int back, const char *action_label)
{
ncm_rect_t r = ncm_topbar_rect();
meow_widget *tb = meow_topappbar_create(r.x, r.y, r.w, r.h, title);
meow_topappbar_set_back(tb, back);
if (action_label) meow_topappbar_add_action(tb, action_label);
meow_widget_set_z(tb, 999); /* chrome 置顶(§6.2:> 一切自动 z) */
return tb;
}
/* 空态/错误:居中两行(kind 大字 + what 小字)。kind 用品牌红(§3.1 品牌元素走 App 调色板)。 */
void ncm_draw_empty(ncm_rect_t r, const char *kind, const char *what)
{
const meow_theme *th = meow_theme_get();
const ncm_pal_t *pa = ncm_palette(g_dark);
int cy = r.y + r.h / 2;
meow_ui_text_center(r.x + r.w / 2, cy - 34, kind ? kind : "", 28, pa->red);
if (what && what[0])
meow_ui_text_center(r.x + r.w / 2, cy + 4, what, 20, th->onSurfaceVariant);
}
/* 底部信息(右对齐 dim)。 */
void ncm_draw_footer(ncm_rect_t r, const char *text)
{
const meow_theme *th = meow_theme_get();
if (!text || !text[0]) return;
meow_ui_text_right(r.x + r.w - APP_PAD, r.y + r.h - 26, text, 20, th->dim);
}
/* 底部提示条(非阻塞):画在内容区底部之上,2s 后由 tick 清除。
* 只画一次并标脏;g_snack_until 为过期时间(0=无)。 */
static char s_snack[192];
static uint64_t s_snack_until; /* monotonic ms 过期时刻 */
static ncm_rect_t s_snack_r;
static int s_snack_id; /* <0=未注册 */
static void snack_draw(void *ctx)
{
(void)ctx;
if (meow_ui_time_ms() >= s_snack_until && s_snack_until != 0) {
meow_ui_invalidate(s_snack_r.x, s_snack_r.y, s_snack_r.w, s_snack_r.h);
s_snack_until = 0;
return;
}
const meow_theme *th = meow_theme_get();
/* 深色底 + 白字 */
meow_ui_fill_rect(s_snack_r.x, s_snack_r.y, s_snack_r.w, s_snack_r.h, th->inverseSurface);
meow_ui_text_center(s_snack_r.x + s_snack_r.w / 2, s_snack_r.y + (s_snack_r.h - 26) / 2 + 4,
s_snack, 20, th->inverseOnSurface);
}
void ncm_snack(const char *text)
{
if (!text) return;
strncpy(s_snack, text, sizeof s_snack - 1);
s_snack[sizeof s_snack - 1] = 0;
s_snack_until = meow_ui_time_ms() + 2000;
int w = meow_ui_width() - 2 * APP_PAD;
s_snack_r = NR(APP_PAD, meow_ui_height() - 56, w, 40);
if (s_snack_id < 0)
s_snack_id = meow_draw_custom_register(999, s_snack_r.x, s_snack_r.y, s_snack_r.w,
s_snack_r.h, snack_draw, NULL);
else
meow_draw_set_z(s_snack_id, 999);
meow_ui_invalidate(s_snack_r.x, s_snack_r.y, s_snack_r.w, s_snack_r.h);
}
/* 周期检查:snack 到期清除。可由各页 tick 调。 */
void chrome_tick(void)
{
if (s_snack_until && meow_ui_time_ms() >= s_snack_until) {
meow_ui_invalidate(s_snack_r.x, s_snack_r.y, s_snack_r.w, s_snack_r.h);
s_snack_until = 0;
}
}
/* ---- §20 音质档位显示名/规格(八档 UI 权威口径,NCM_CACHE_DESIGN §3;settings/player 共用)----
* 档位中文名以我方设计文档为准(BIZ_NOTES §2.1:CNMPlayer 标签只做 level 串对齐参考)。 */
const char *ncm_level_name(int lv)
{
switch (lv) {
case NCM_LEVEL_SURROUND: return "沉浸环绕声";
case NCM_LEVEL_SKY: return "臻音全景声";
case NCM_LEVEL_JYMASTER: return "超清母带";
case NCM_LEVEL_JYEFFECT: return "高清臻音";
case NCM_LEVEL_DOLBY: return "杜比全景声"; /* 映射保留,不进 UI 清单 */
case NCM_LEVEL_HIRES: return "Hi-Res";
case NCM_LEVEL_LOSSLESS: return "无损 SQ";
case NCM_LEVEL_EXHIGH: return "极高 HQ";
case NCM_LEVEL_HIGHER: return "较高"; /* 仅降级链中间档,不进 UI */
default: return "标准";
}
}
const char *ncm_level_spec(int lv)
{
switch (lv) {
case NCM_LEVEL_SURROUND: return "5.1 声道";
case NCM_LEVEL_SKY: return "Audio Vivid";
case NCM_LEVEL_JYMASTER: return "192kHz/24bit";
case NCM_LEVEL_JYEFFECT: return "96kHz/24bit";
case NCM_LEVEL_DOLBY: return "Dolby Atmos";
case NCM_LEVEL_HIRES: return "192kHz/24bit";
case NCM_LEVEL_LOSSLESS: return "48kHz/16bit";
case NCM_LEVEL_EXHIGH: return "≤320kbps";
case NCM_LEVEL_HIGHER: return "192kbps";
default: return "128kbps";
}
}