feat(ncm): 网易云客户端 v1 UI+播放链路 (agent-205) — os.meow.ncm: 登录页(真实unikey二维码+轮询状态机)/我的歌单(listview)/歌单详情/播放页(封面+seek+滚动歌词+竖横屏切换)/设置(音质默认Hi-res+缓存上限默认10G); 播放线程(缓存LRU+minimp3→ALSA UAPI直开hw:0,0); 媒体会话MSG_META上报+PLAYCTL接收(首真实媒体源)
ncm 1e331964 (text 764KB); 0 warning; 宿主离屏6页截图目检+真实QR可扫+qemu-mipsel干净启动+解码446xRT 真机待联调: ALSA时序/803扫码闭环/媒体会话联动/gbk大字库渲染
This commit is contained in:
1 parent
32543ac616
commit
21519ce2e4
23 files changed
+3255
No files matched your search
@@ -0,0 +1,193 @@
|
||||
/* ui/settings.c — 设置页(ACT_SETTINGS)。
|
||||
*
|
||||
* 行项:音质(Hi-res/较高/标准,默认 Hi-res)+ 缓存上限(1~100GB,默认 10GB)+ 退出登录。
|
||||
* 设置持久化到 MeowData/os.meow.ncm/data(meow_config_*;缓存上限键 ncm.cache_limit_gb 与核心库
|
||||
* ncm_cache_load_setting 对齐)。音质/上限经 BottomSheet 选择;退出登录经 confirm 弹窗。
|
||||
* 空态/绘制用 custom 绘制物 + 手动行命中(仅 3 行,无需列表滚动)。
|
||||
*/
|
||||
#include "../ncm_app_internal.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
typedef struct { meow_view *view; int active; } settings_ctrl;
|
||||
static settings_ctrl g_sc;
|
||||
|
||||
static meow_widget *g_tb;
|
||||
static ncm_rect_t r_cont;
|
||||
static int g_custom_id;
|
||||
static int g_row_h; /* 行高 */
|
||||
|
||||
static const char *level_name(int lv)
|
||||
{
|
||||
switch (lv) {
|
||||
case NCM_LEVEL_HIRES: return "Hi-Res";
|
||||
case NCM_LEVEL_LOSSLESS:return "Lossless";
|
||||
case NCM_LEVEL_EXHIGH: return "Exhigh";
|
||||
case NCM_LEVEL_HIGHER: return "Higher";
|
||||
default: return "Standard";
|
||||
}
|
||||
}
|
||||
|
||||
static void settings_draw(void *ctx)
|
||||
{
|
||||
(void)ctx;
|
||||
if (meow_ui_activity_active() != ACT_SETTINGS) return;
|
||||
const meow_theme *th = meow_theme_get();
|
||||
g_row_h = 60;
|
||||
int y = r_cont.y + 16;
|
||||
int w = r_cont.w - 2 * APP_PAD;
|
||||
int x = APP_PAD;
|
||||
|
||||
/* 行:音质 / 缓存上限 / 退出登录 */
|
||||
struct { const char *title; char sub[48]; } rows[3];
|
||||
rows[0].title = "Music Quality";
|
||||
rows[1].title = "Cache Limit";
|
||||
rows[2].title = "Sign Out";
|
||||
snprintf(rows[0].sub, sizeof rows[0].sub, "current: %s", level_name(g_st.level));
|
||||
snprintf(rows[1].sub, sizeof rows[1].sub, "current: %d GB", g_st.cache_limit_gb);
|
||||
snprintf(rows[2].sub, sizeof rows[2].sub, "%s", g_st.nickname[0] ? g_st.nickname : "sign out");
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
int yy = y + i * (g_row_h + 8);
|
||||
meow_ui_fill_rect(x, yy, w, g_row_h, th->surfaceContainer);
|
||||
uint32_t tcol = (i == 2) ? th->error : th->fg;
|
||||
uint32_t scol = (i == 2) ? th->error : th->dim;
|
||||
meow_ui_text(x + APP_PAD, yy + 12, rows[i].title, 20, tcol);
|
||||
meow_ui_text(x + APP_PAD, yy + 34, rows[i].sub, 20, scol);
|
||||
/* 右侧箭头 (draw) */
|
||||
meow_ui_text_right(x + w - APP_PAD, yy + 18, ">", 28, th->dim);
|
||||
}
|
||||
}
|
||||
|
||||
static int row_at(int x, int y)
|
||||
{
|
||||
(void)x;
|
||||
int r = r_cont.y + 16;
|
||||
for (int i = 0; i < 3; i++) {
|
||||
int yy = r + i * (g_row_h + 8);
|
||||
if (y >= yy && y <= yy + g_row_h) return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int settings_is_active(void) { return g_sc.active; }
|
||||
|
||||
int settings_on_tap(int x, int y)
|
||||
{
|
||||
if (g_tb) {
|
||||
int ah = meow_topappbar_hit(g_tb, x, y);
|
||||
if (ah == MEOW_TOPBAR_BACK) { meow_view_page_pop(); return 1; }
|
||||
if (ah >= 0) { return 1; }
|
||||
}
|
||||
if (x < APP_PAD || x > r_cont.w - APP_PAD) return 0;
|
||||
int row = row_at(x, y);
|
||||
if (row < 0) return 0;
|
||||
|
||||
if (row == 0) { /* 音质 */
|
||||
meow_sheet *s = meow_sheet_create("Music Quality", 0);
|
||||
meow_sheet_add_item(s, "Hi-Res");
|
||||
meow_sheet_add_item(s, "Higher");
|
||||
meow_sheet_add_item(s, "Standard");
|
||||
int op = meow_sheet_open(s);
|
||||
meow_sheet_free(s);
|
||||
if (op >= 0) {
|
||||
g_st.level = (op == 0) ? NCM_LEVEL_HIRES : (op == 1) ? NCM_LEVEL_HIGHER : NCM_LEVEL_STANDARD;
|
||||
player_manager_set_quality(g_st.level, g_st.cache_limit_gb);
|
||||
appstate_save_settings();
|
||||
meow_ui_invalidate(r_cont.x, r_cont.y, r_cont.w, r_cont.h);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
if (row == 1) { /* 缓存上限 */
|
||||
static const int gbs[] = { 1, 2, 5, 10, 20, 30, 50, 100 };
|
||||
static const char *labels[] = { "1 GB", "2 GB", "5 GB", "10 GB",
|
||||
"20 GB", "30 GB", "50 GB", "100 GB" };
|
||||
meow_sheet *s = meow_sheet_create("Cache Limit", 0);
|
||||
for (int i = 0; i < 8; i++) meow_sheet_add_item(s, labels[i]);
|
||||
int op = meow_sheet_open(s);
|
||||
meow_sheet_free(s);
|
||||
if (op >= 0 && op < 8) {
|
||||
g_st.cache_limit_gb = gbs[op];
|
||||
player_manager_set_quality(g_st.level, g_st.cache_limit_gb);
|
||||
appstate_save_settings();
|
||||
meow_ui_invalidate(r_cont.x, r_cont.y, r_cont.w, r_cont.h);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
if (row == 2) { /* 退出登录 */
|
||||
meow_dialog *d = meow_dialog_create(MEOW_DLG_CONFIRM, "Sign out?");
|
||||
const char *msgs[1];
|
||||
msgs[0] = "Playback will stop.\nYou'll need to scan a QR code again.";
|
||||
meow_dialog_set_items(d, msgs, 1, -1);
|
||||
int yes = meow_dialog_confirm(d);
|
||||
meow_dialog_free(d);
|
||||
if (yes) {
|
||||
appstate_logout();
|
||||
meow_view_page_replace(ACT_LOGIN);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void settings_swipe(int dir, int dist) { (void)dir; (void)dist; }
|
||||
void settings_touch_down(int x, int y) { (void)x; (void)y; }
|
||||
void settings_touch_move(int x, int y) { (void)x; (void)y; }
|
||||
|
||||
void settings_open(void)
|
||||
{
|
||||
g_st.playing_song = -1;
|
||||
meow_view_page_push(ACT_SETTINGS);
|
||||
}
|
||||
|
||||
void settings_reload(void)
|
||||
{
|
||||
const meow_theme *th = meow_theme_get();
|
||||
meow_ui_set_page_bg(th->bg);
|
||||
meow_ui_paint_full();
|
||||
}
|
||||
|
||||
static void st_page_create(meow_view *v, void *ctx)
|
||||
{
|
||||
(void)v;
|
||||
((settings_ctrl *)ctx)->view = v;
|
||||
r_cont = ncm_content_rect();
|
||||
meow_ui_activity_push(ACT_SETTINGS); /* owner=ACT_SETTINGS */
|
||||
g_tb = ncm_topbar_create("Settings", 1, NULL);
|
||||
meow_ui_activity_pop();
|
||||
g_row_h = 60;
|
||||
g_custom_id = meow_draw_custom_register(1, r_cont.x, r_cont.y, r_cont.w, r_cont.h,
|
||||
settings_draw, NULL);
|
||||
}
|
||||
|
||||
static void st_page_enter(meow_view *v, void *ctx)
|
||||
{
|
||||
(void)v;
|
||||
((settings_ctrl *)ctx)->active = 1;
|
||||
settings_reload();
|
||||
}
|
||||
|
||||
static void st_page_leave(meow_view *v, void *ctx)
|
||||
{
|
||||
(void)v;
|
||||
((settings_ctrl *)ctx)->active = 0;
|
||||
}
|
||||
|
||||
static void st_page_destroy(meow_view *v, void *ctx)
|
||||
{
|
||||
(void)v; (void)ctx;
|
||||
if (g_custom_id) { meow_draw_unregister(g_custom_id); g_custom_id = 0; }
|
||||
if (g_tb) { meow_widget_free(g_tb); g_tb = NULL; }
|
||||
}
|
||||
|
||||
meow_view *settings_view_create(void)
|
||||
{
|
||||
static const meow_view_hooks h = {
|
||||
.on_create = st_page_create,
|
||||
.on_enter = st_page_enter,
|
||||
.on_leave = st_page_leave,
|
||||
.on_destroy = st_page_destroy,
|
||||
};
|
||||
return meow_view_create(ACT_SETTINGS, MEOW_VIEW_ACTIVITY, &h, &g_sc);
|
||||
}
|
||||
Reference in new issue
Block a user