# -*- coding: utf-8 -*- r'''手表端:输入配对码连上之后,先到一个「选择页」。 == 用户要求(原话)== "安卓那个逻辑也要改一下,就是你输入配对码后,进入一个页面, 页面有两个按钮,第一个按钮是远控,第二个按钮是静默更新被控端" == 现状 == 连接成功后直接 `showControlPanel()`(进控制页)。用户根本没机会 先升级被控端 —— 而老版本的被控端可能缺功能、有 bug, "先更新再远控"其实是更合理的顺序。 == 改法 == 新增一个 `choicePanel`: ┌──────────────────────────┐ │ 已连接「电脑名」 │ │ 电脑端版本 2.1.5 │ <- 一眼看到要不要更新 │ │ │ ┌────────────────────┐ │ │ │ 远控 │ │ <- 第一个按钮 │ └────────────────────┘ │ │ ┌────────────────────┐ │ │ │ 静默更新被控端 │ │ <- 第二个按钮 │ └────────────────────┘ │ └──────────────────────────┘ **按钮顺序按用户说的来**:远控在前(主操作),更新在后。 之后: * 连接成功(`showControlPanel()` 那个调用点)改成 `showChoicePanel()` * **自动切中转恢复**那个调用点**不改** —— 用户本来就在远控页, 中途弹一个选择页会打断他 * 「静默更新被控端」复用已经做好的 `checkPcUpdate()` (查版本 -> 确认框 -> 触发更新) * 版本号显示在这一页上:用户说"手表端连接那个电脑端也可以查看 电脑端目前所处的版本",这里是最自然的展示位置 == 一些细节 == * 顶部快捷栏在这一页照样在(用户要求"不管干什么上面都有一栏") * 面板要加进 `showOnly` 的数组,否则它会永远压在最上面 * 也要 `applyPanelTopMargin`,否则顶部被快捷栏盖住 * **版本号是异步查的**:连上后立刻查一次;查回来之前显示"正在查…"。 不查的话用户不知道要不要点更新。 ''' from __future__ import annotations import sys from pathlib import Path ROOT = Path(__file__).resolve().parent.parent MAIN = ROOT / "android" / "src" / "com" / "lanremote" / "watch" / "MainActivity.java" # ---- 1. 字段 ---- F_ANCHOR = " private FrameLayout mousePanel;" F_NEW = ''' private FrameLayout mousePanel; /** * 连上之后先给用户看的那一页:远控 / 静默更新被控端。 * *

用户要求:"输入配对码后,进入一个页面,页面有两个按钮, * 第一个按钮是远控,第二个按钮是静默更新被控端"。 * *

为什么不让它直接进控制页:老版本的被控端可能缺功能或者有 bug, * "先更新再远控"是更合理的顺序 —— 但也不能强制,所以给用户选。 */ private FrameLayout choicePanel; /** 选择页上显示"电脑端版本 X.Y.Z"那一行。 */ private TextView choiceVersionText; /** 选择页上显示"已连接 XXX"。 */ private TextView choiceWhoText;''' # ---- 2. 建面板的方法,插在 showOnly 之前 ---- M_ANCHOR = " private void showOnly(View keep) {" M_NEW = ''' // ================================================================== // 连接后的选择页(远控 / 静默更新) // ================================================================== /** * 建选择页。 * *

布局从上到下:连接信息 -> 版本 -> [远控] -> [静默更新被控端]。 * 按钮顺序就是用户说的顺序(远控在前)。 */ private void buildChoicePanel() { choicePanel = new FrameLayout(this); choicePanel.setBackgroundColor(C_BG); LinearLayout box = new LinearLayout(this); box.setOrientation(LinearLayout.VERTICAL); box.setPadding(dp(14), dp(20), dp(14), dp(14)); box.setGravity(android.view.Gravity.CENTER_HORIZONTAL); choicePanel.addView(box, new FrameLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); choiceWhoText = new TextView(this); choiceWhoText.setText("已连接"); choiceWhoText.setTextColor(C_FG); choiceWhoText.setTextSize(14f); choiceWhoText.setGravity(android.view.Gravity.CENTER); box.addView(choiceWhoText); choiceVersionText = new TextView(this); choiceVersionText.setText("电脑端版本:正在查…"); choiceVersionText.setTextColor(C_DIM); choiceVersionText.setTextSize(12f); choiceVersionText.setGravity(android.view.Gravity.CENTER); choiceVersionText.setPadding(0, dp(4), 0, dp(10)); box.addView(choiceVersionText); // 第一个按钮:远控 Button ctrl = bigButton("远控", C_OK, 17); LinearLayout.LayoutParams ctrlLp = new LinearLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, dp(54)); ctrlLp.setMargins(0, 0, 0, dp(10)); ctrl.setLayoutParams(ctrlLp); ctrl.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { showControlPanel(); } }); box.addView(ctrl); // 第二个按钮:静默更新被控端 Button upd = bigButton("静默更新被控端", C_ACCENT, 15); LinearLayout.LayoutParams updLp = new LinearLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, dp(54)); upd.setLayoutParams(updLp); upd.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // 复用已经做好的那条链路:查版本 -> 确认 -> 触发更新 checkPcUpdate(); } }); box.addView(upd); TextView tip = new TextView(this); tip.setText("更新时电脑会自动下载并静默安装," + "不会弹任何窗口,设置和配对码都不变。"); tip.setTextColor(C_DIM); tip.setTextSize(10f); tip.setGravity(android.view.Gravity.CENTER); tip.setPadding(0, dp(10), 0, 0); box.addView(tip); } /** 显示选择页,并顺手查一下电脑端版本。 */ private void showChoicePanel() { if (choicePanel == null) { buildChoicePanel(); } if (choiceWhoText != null) { String who = lastDeviceName; choiceWhoText.setText(who == null || who.isEmpty() ? "已连接" : "已连接「" + who + "」"); } if (choiceVersionText != null) { choiceVersionText.setText("电脑端版本:正在查…"); } showOnly(choicePanel); queryPcVersionForChoice(); } /** * 查电脑端版本填到选择页上。 * *

单独写一个(不直接用 checkPcUpdate)是因为那个会弹确认框 —— * 这里只是"让你看一眼版本",不该弹东西。 */ private void queryPcVersionForChoice() { io.execute(new Runnable() { @Override public void run() { Map reply; try { reply = client.call(Json.obj("cmd", "update", "action", "check"), 25000); } catch (final Throwable error) { reply = null; } final String text; if (reply != null && Boolean.TRUE.equals(reply.get("ok"))) { String cur = String.valueOf(reply.get("current")); String latest = String.valueOf(reply.get("latest")); text = Boolean.TRUE.equals(reply.get("needUpdate")) ? "电脑端 " + cur + " → 可更新到 " + latest : "电脑端 " + cur + "(已是最新)"; } else { text = "电脑端版本:查不到(可以点下面的更新按钮再试)"; } ui.post(new Runnable() { @Override public void run() { if (choiceVersionText != null) { choiceVersionText.setText(text); } } }); } }); } ''' # ---- 3. showOnly 数组 ---- O_SO = ''' View[] all = {connectPanel, controlPanel, mirrorPanel, lanPanel, settingsPanel, mousePanel};''' N_SO = ''' View[] all = {connectPanel, controlPanel, mirrorPanel, lanPanel, settingsPanel, mousePanel, choicePanel};''' # ---- 4. onCreate:建 + 挂 + 让位 ---- O_OC = ''' loadQuickSlots(); buildMousePanel(); // 只建,先不显示(showOnly 会切) buildQuickBar();''' N_OC = ''' loadQuickSlots(); buildMousePanel(); // 只建,先不显示(showOnly 会切) buildChoicePanel(); // 同上:连上之后才显示 buildQuickBar();''' O_OC2 = ''' applyPanelTopMargin(mousePanel);''' N_OC2 = ''' applyPanelTopMargin(mousePanel); applyPanelTopMargin(choicePanel);''' O_OC3 = ''' root.addView(mousePanel, new FrameLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); mousePanel.setVisibility(View.GONE);''' N_OC3 = ''' root.addView(mousePanel, new FrameLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); mousePanel.setVisibility(View.GONE); root.addView(choicePanel, new FrameLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); choicePanel.setVisibility(View.GONE);''' # ---- 5. 连接成功后改成进选择页 ---- O_CONN = ''' showControlPanel(); appendLog("已连接到 " + host); toast("连接成功");''' N_CONN = ''' // 用户要求:输入配对码连上之后先到选择页 // (远控 / 静默更新被控端),不直接进控制页。 // // ⚠ 只有**这个**调用点改。下面"自动切到中转恢复" // 那个 showControlPanel() 不改 —— 用户本来就在 // 远控页,中途弹选择页会打断他。 showChoicePanel(); appendLog("已连接到 " + host); toast("连接成功");''' PAIRS = [ ("字段", F_ANCHOR, F_NEW), ("建面板方法", M_ANCHOR, M_NEW + M_ANCHOR), ("showOnly 数组", O_SO, N_SO), ("onCreate 建面板", O_OC, N_OC), ("onCreate 让位", O_OC2, N_OC2), ("onCreate 挂面板", O_OC3, N_OC3), ("连接成功后进选择页", O_CONN, N_CONN), ] def main() -> int: print("=" * 58) print(" 手表端:连上后先到「远控 / 静默更新」选择页") print("=" * 58) print() raw = MAIN.read_bytes() bom = raw.startswith(b"\xef\xbb\xbf") t = raw.decode("utf-8-sig") if "buildChoicePanel" in t: print(" 已经改过了") return 0 missing = [(n, o.strip().splitlines()[0][:60]) for n, o, _w in PAIRS if o not in t] if missing: print(" ✗ 这些锚点找不到:") for n, s in missing: print(" %s: %s" % (n, s)) return 1 print(" ✓ %d 个锚点全部就位" % len(PAIRS)) for _n, old, new in PAIRS: t = t.replace(old, new, 1) MAIN.write_bytes((b"\xef\xbb\xbf" if bom else b"") + t.encode("utf-8")) print(" ✓ 加了 choicePanel + buildChoicePanel + showChoicePanel") print(" ✓ 连接成功后改成进选择页(自动切中转那个没动)") # 复核:确认"自动切中转"那个调用点没被误改 back = MAIN.read_text(encoding="utf-8") n_choice = back.count("showChoicePanel();") n_ctrl = back.count("showControlPanel();") print() print(" 复核:showChoicePanel 调用 %d 处(应 >=2:定义内 + 连接成功处)" % n_choice) print(" showControlPanel 调用 %d 处(自动切中转那个应该还在)" % n_ctrl) if "已切到中转服务器,连接恢复" in back: i = back.find("已切到中转服务器,连接恢复") seg = back[i:i + 200] ok = "showControlPanel();" in seg print(" [%s] 自动切中转恢复后仍是 showControlPanel" % ("PASS" if ok else "FAIL")) if not ok: return 1 return 0 if __name__ == "__main__": sys.exit(main())