# -*- coding: utf-8 -*- """安卓端:局域网断了就无缝切回中转服务器。 用户要求: "检测到局域网连不上了,就无缝链接到中转服务器" 场景: 手表和电脑本来在同一 WiFi,走局域网直连(快、稳)。 然后手表走出 WiFi 范围了 / 电脑换了网络 / WiFi 断了。 → 应该**自动**切回中转,用户不用管。 做法: 1. 记住"当前是局域网直连"以及连的是谁 2. onDisconnected 时,如果刚才走的是局域网 -> 自动经中转重连 3. 反过来:中转也断了就重试几次,别立刻放弃 4. 全程给用户提示,但不能弹回连接页打断操作 """ from __future__ import annotations import sys from pathlib import Path MAIN = Path(r"C:\Users\shu\Desktop\deepseekagent\LanRemote\android\src" r"\com\lanremote\watch\MainActivity.java") # ---------- 1. 加状态字段 ---------- FIELD_OLD = ''' private void connect(final String host, final int port, final String code, final String token, final String key) { if (busy) { return; }''' FIELD_NEW = ''' // ---- 断线自动切换用的状态 ---- /** 当前是不是走局域网直连(断了要切回中转)。 */ private volatile boolean lanDirect; /** 刚才连的地址/配对码/令牌,切回中转时要用。 */ private volatile String lastHost = ""; private volatile int lastPort; private volatile String lastCode = ""; private volatile String lastToken = ""; /** 正在自动切换中,避免重入。 */ private volatile boolean autoSwitching; /** 自动重试次数,防止无限刷屏。 */ private int autoRetry; private void connect(final String host, final int port, final String code, final String token, final String key) { if (busy) { return; }''' # ---------- 2. connect 时记录状态 ---------- REC_OLD = ''' busy = true; setStatus("正在连接 " + host + ":" + port + " …", C_WARN); appendLog("连接 " + host + ":" + port);''' REC_NEW = ''' busy = true; // 记下来:断线时要用这些信息切回中转 / 重连 lastHost = host; lastPort = port; if (code != null) { lastCode = code; } if (token != null) { lastToken = token; } setStatus("正在连接 " + host + ":" + port + " …", C_WARN); appendLog("连接 " + host + ":" + port);''' # ---------- 3. 成功连上后标记是不是局域网 ---------- MODE_OLD = ''' if (serverLabel != null) { serverLabel.setText("已连接 " + (finalName == null ? host : finalName)); }''' MODE_NEW = ''' // 记住这次是不是局域网直连 —— // 局域网断了要自动切回中转,靠的就是这个标记。 lanDirect = finalLanDirect; autoRetry = 0; if (serverLabel != null) { serverLabel.setText("已连接 " + (finalName == null ? host : finalName) + (finalLanDirect ? "(局域网)" : "(中转)")); }''' # 把"是不是局域网"这个判断结果传出来 LAN_FLAG_OLD = ''' String error = null; String newToken = null; String serverName = null; boolean ok = false;''' LAN_FLAG_NEW = ''' String error = null; String newToken = null; String serverName = null; boolean ok = false; // 这次连上的是局域网还是中转(决定断线后怎么救) boolean usedLan = !relayUrl().isEmpty() ? false : true;''' # 直连成功时置 true DIRECT_OK_OLD = ''' appendLog("已切换为局域网直连:" + directIp + ":" + directPort);''' DIRECT_OK_NEW = ''' appendLog("已切换为局域网直连:" + directIp + ":" + directPort); usedLan = true;''' # 退回中转时置 false FALLBACK_OLD = ''' appendLog("直连失败,退回中转:" + Json.str(direct, "error", "未知原因"));''' FALLBACK_NEW = ''' appendLog("直连失败,退回中转:" + Json.str(direct, "error", "未知原因")); usedLan = false;''' # final 变量 FINAL_OLD = ''' final boolean success = ok; final String finalError = error; final String finalToken = newToken; final String finalName = serverName;''' FINAL_NEW = ''' final boolean success = ok; final String finalError = error; final String finalToken = newToken; final String finalName = serverName; final boolean finalLanDirect = usedLan && ok;''' # ---------- 4. onDisconnected 自动切回中转 ---------- DISC_OLD = ''' public void onDisconnected(final String reason) { // 换连另一台电脑后,「功能」页不能还显示上一台的分组 featureCache = null; featureGroups = null; ui.post(new Runnable() {''' DISC_NEW = ''' public void onDisconnected(final String reason) { // 换连另一台电脑后,「功能」页不能还显示上一台的分组 featureCache = null; featureGroups = null; // ★ 局域网断了 -> 无缝切回中转服务器。 // 用户从 WiFi 走出去、或者电脑换了网络,都会走到这里。 // 不打断操作,自动接上。 if (lanDirect && !autoSwitching) { String relay = relayUrl(); if (!relay.isEmpty()) { autoSwitching = true; appendLog("局域网断了(" + reason + "),正在切到中转服务器…"); ui.post(new Runnable() { @Override public void run() { setStatus("局域网断了,正在切到中转服务器…", C_WARN); } }); ui.postDelayed(new Runnable() { @Override public void run() { switchToRelay(); } }, 600); return; } appendLog("局域网断了,但没有配中转地址,只能断开"); } // 中转也断了:重试几次再放弃(这条线路本来就会抖) if (!lanDirect && autoRetry < 3 && !autoSwitching) { autoRetry++; appendLog("中转断了(" + reason + "),第 " + autoRetry + " 次自动重连…"); ui.post(new Runnable() { @Override public void run() { setStatus("中转断了,正在重连(第 " + autoRetry + " 次)…", C_WARN); } }); ui.postDelayed(new Runnable() { @Override public void run() { switchToRelay(); } }, 700 * autoRetry); return; } autoRetry = 0; ui.post(new Runnable() {''' DISC_TAIL_OLD = ''' appendLog("断开:" + reason); } }); }''' DISC_TAIL_NEW = ''' appendLog("断开:" + reason); lanDirect = false; } }); } /** * 经中转重连(局域网断了、或者中转抖了之后自动调用)。 * * 用后台线程做,不阻塞界面;成功了就继续用,失败才弹回连接页。 */ private void switchToRelay() { final String relay = relayUrl(); if (relay.isEmpty()) { autoSwitching = false; return; } final String code = lastCode; final String token = lastToken; io.execute(new Runnable() { @Override public void run() { String error = null; boolean ok = false; String newToken = token; String name = lastHost; try { Map reply = client.connectViaRelay( relay, code, token, MainActivity.this, 40000); ok = Json.bool(reply, "ok", false); if (ok) { newToken = Json.str(reply, "token", token == null ? "" : token); name = Json.str(reply, "serverName", lastHost); } else { error = Json.str(reply, "error", "中转连接被拒"); } } catch (final IOException exc) { error = describeNetworkError(exc, relay); } final boolean success = ok; final String finalError = error; final String finalToken = newToken; final String finalName = name; final String usedCode = code; ui.post(new Runnable() { @Override public void run() { autoSwitching = false; if (success) { lanDirect = false; lastToken = finalToken; if (finalToken != null && !finalToken.isEmpty()) { store.saveDevice( DeviceStore.deviceKey(lastHost, lastPort, store.deviceId()), finalName == null ? lastHost : finalName, lastHost, lastPort, finalToken); refreshDeviceList(); } if (serverLabel != null) { serverLabel.setText("已连接 " + (finalName == null ? lastHost : finalName) + "(中转)"); } appendLog("已切到中转服务器,连接恢复"); setStatus("已切到中转(局域网不可用)", C_OK); showControlPanel(); } else { autoRetry = 0; lanDirect = false; setStatus("中转也连不上:" + finalError, C_DANGER); appendLog("切中转失败:" + finalError); showConnectPanel(); } } }); } }); }''' def patch(pairs, label: str) -> bool: text = MAIN.read_text(encoding="utf-8") ok = True for old, new, tag in pairs: if old not in text: print(f" [{label}] [跳过] {tag} —— 锚点没找到") ok = False continue text = text.replace(old, new, 1) print(f" [{label}] {tag}") MAIN.write_text(text, encoding="utf-8", newline="\n") return ok def main() -> int: text = MAIN.read_text(encoding="utf-8") if "switchToRelay" in text: print(" 已经加过自动切中转") return 0 ok = patch([ (FIELD_OLD, FIELD_NEW, "加自动切换状态字段"), (REC_OLD, REC_NEW, "connect 记录状态"), (LAN_FLAG_OLD, LAN_FLAG_NEW, "加 usedLan 标记"), (DIRECT_OK_OLD, DIRECT_OK_NEW, "直连成功置 true"), (FALLBACK_OLD, FALLBACK_NEW, "退回中转置 false"), (FINAL_OLD, FINAL_NEW, "传出 finalLanDirect"), (MODE_OLD, MODE_NEW, "连上后标记模式"), (DISC_OLD, DISC_NEW, "断线自动切中转"), (DISC_TAIL_OLD, DISC_TAIL_NEW, "switchToRelay 方法"), ], "MainActivity") return 0 if ok else 1 if __name__ == "__main__": sys.exit(main())