# -*- coding: utf-8 -*- r'''手表端加「音效」—— 用现成的 showChooser(),不新建面板。 == 为什么改方案 == 第一版我给音效页写了完整的新面板(FrameLayout + ScrollView + 动态列表), 后来发现 MainActivity 里**已经有 showChooser(title, labels, Chooser)** 这个现成的选择器 —— pickVoice() 就是这么选 TTS 音色的。 用选择器的话: * 不用新建面板 -> 不用碰 root.addView / showOnly(那两个地方 刚因为"漏挂"出过一次全白的事故) * 代码量少一半,出错面小得多 * 而且和"选音色"的操作手感一致,用户不用学两套 所以改成:首页加一个「音效」按钮 -> 弹出音效列表 -> 点哪个放哪个。 == 服务端配合 == 服务端返回的是 {"sfx":[{"name":...},...]} 这种嵌套结构, 而手表的 stringList(reply, key) 只认扁平的字符串数组。 audio_list 早就这么做了(同时返回 files / names / paths), 这里跟着加一个 "names",手表直接用。 ''' from __future__ import annotations import sys from pathlib import Path ROOT = Path(__file__).resolve().parent.parent SRC = (ROOT / "android" / "src" / "com" / "lanremote" / "watch" / "MainActivity.java") # ---------------------------------------------------------------- 方法 METHOD_ANCHOR = " /** 选择电脑上的 TTS 音色。 */" METHOD_NEW = ''' /** 让电脑放一个音效(网络热门音效那类)。 * * 用 showChooser 而不是新建面板 —— 见 _build/add_watch_sfx.py 里的说明: * pickVoice() 已经是这个套路,复用省事也更不容易出错。 */ private void pickSfx() { appendLog("正在获取音效…"); io.execute(new Runnable() { @Override public void run() { try { final Map reply = client.call(Json.obj("cmd", "sfx_list"), 15000); // 服务端同时给了扁平的 names(和 audio_list 一个约定) final List names = stringList(reply, "names"); final String dir = Json.str(reply, "dir", ""); ui.post(new Runnable() { @Override public void run() { if (names.isEmpty()) { toast("电脑上还没有音效(或没连上)"); appendLog("音效列表是空的。" + (dir.isEmpty() ? "" : "素材目录:" + dir)); return; } showChooser("音效(点一下电脑就响)", names, new Chooser() { @Override public void onPick(int index) { String one = names.get(index); sendCommand(Json.obj("cmd", "sfx_play", "name", one), "音效 " + one); } }); } }); } catch (final IOException e) { postError("获取音效", e); } } }); } ''' + METHOD_ANCHOR def main() -> int: text = SRC.read_text(encoding="utf-8") out: list = [] def patch(old: str, new: str, what: str) -> None: nonlocal text if old not in text: out.append(("✗", what, "找不到锚点")) return if text.count(old) != 1: out.append(("!", what, "锚点 %d 处" % text.count(old))) text = text.replace(old, new, 1) out.append(("✓", what, "")) # 1) 插方法 patch(METHOD_ANCHOR, METHOD_NEW, "插入 pickSfx()") # 2) 首页加按钮(放在「音效」该在的位置:设置按钮之后) patch(''' box.addView(sfxButton);''', ''' box.addView(sfxButton);''', "(占位,见下)") if False else None patch(''' box.addView(relayButton);''', ''' box.addView(relayButton); // 音效:点一下电脑就响。用户要求"可以直接远程播放"。 Button sfxButton = bigButton("音效(让电脑放一声)", C_ACCENT, 14); LinearLayout.LayoutParams sfxParams = new LinearLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, dp(52)); sfxParams.setMargins(0, 0, 0, dp(8)); sfxButton.setLayoutParams(sfxParams); sfxButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { pickSfx(); } }); box.addView(sfxButton);''', "首页加「音效」按钮") print(" ── 补丁结果 ──") for mark, what, why in out: print(" %s %s%s" % (mark, what, (" —— " + why) if why else "")) if any(m == "✗" for m, _w, _y in out): print() print(" 有锚点没匹配,没有写回") return 1 SRC.write_text(text, encoding="utf-8", newline="\n") print() print(" 已写回 MainActivity.java") return 0 if __name__ == "__main__": sys.exit(main())