Files
ykxmz/_build/patch_merge_diag.py
T
xmz7891 b62ac865ea ykxmz 1.5.0:修复手表端 TLS 证书校验 + MTU 修复网络稳定性
被控端
- 修复 MTU 问题:夹 MSS + 路径 MTU 探测(公网可用率 48% -> 100%)
- 复用 HTTP 连接(免掉每次新建 TLS 的 30% 握手失败)
- 每分钟回头找局域网,自动切回快 400 倍的路径
- 画面心跳帧:静止时省流量,客户端仍知道流是活的
- 锁屏时给出可操作的提示

手表端
- 内置 GTS Root R4 / GlobalSign Root CA,修 SSLHandshakeException
- 连接错误翻译成人话(证书/时间/解析/超时分别提示)
- 经中转连上后用服务器报的地址刷新已配对记录
- 新增局域网连接页面、服务器延迟检测

服务器端
- 支持 chunked 请求体(修每 16 秒一次的连接错乱与大面积 502)
- 网络调优脚本 net-tune.sh(开机自动应用)
- 管理页:批量删除、网络体检、下载区
- 隧道改 HTTP/2 + 双连接

文档
- 许可协议 / 安装前必读 / 安装后须知(作者:小麦子)
2026-10-06 19:01:05 +08:00

295 lines
11 KiB
Python

# -*- coding: utf-8 -*-
"""把「遥控诊断」合并进手表主程序(不再需要单独装一个诊断 App)。
改动:
1. 新增字段 diagPanel / diagText / diagStatusLine / diagIpInput
2. 新增 buildDiagPanel():全屏浮层,顶部按钮 + 报告正文 + 底部手动测 IP
3. onCreate 里构建并加入 root
4. onBackPressed 优先关诊断面板
5. 控制面板加一个「设备诊断」按钮
6. 新增 openDiagPanel / closeDiagPanel / runDiagnostics / copyDiagReport / testDiagIp
"""
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")
FIELDS_OLD = " private TextView logView;\n"
FIELDS_NEW = (
" private TextView logView;\n"
" private FrameLayout diagPanel;\n"
" private TextView diagText;\n"
" private TextView diagStatusLine;\n"
" private EditText diagIpInput;\n"
" private boolean diagRunning = false;\n"
)
BUILD_CALL_OLD = " buildMirrorPanel();\n"
BUILD_CALL_NEW = " buildMirrorPanel();\n buildDiagPanel();\n"
ADD_VIEW_OLD = " root.addView(mirrorPanel);\n"
ADD_VIEW_NEW = " root.addView(mirrorPanel);\n root.addView(diagPanel);\n"
BACK_OLD = """ if (mirrorPanel.getVisibility() == View.VISIBLE) {
closeMirrorPanel();
return;
}
"""
BACK_NEW = """ if (diagPanel != null && diagPanel.getVisibility() == View.VISIBLE) {
closeDiagPanel();
return;
}
if (mirrorPanel.getVisibility() == View.VISIBLE) {
closeMirrorPanel();
return;
}
"""
BUTTON_OLD = """ action("系统信息", new Runnable() {
public void run() { showInfo(); }
}));
flushGrid(grid);"""
BUTTON_NEW = """ action("系统信息", new Runnable() {
public void run() { showInfo(); }
}));
addRow(grid,
action("设备诊断", new Runnable() {
public void run() { openDiagPanel(); }
}),
action("清空日志", new Runnable() {
public void run() { logView.setText(""); }
}));
flushGrid(grid);"""
ANCHOR = " private void buildMirrorPanel() {"
DIAG_CODE = ''' // ==================================================================
// 设备诊断(原来是一个单独的「遥控诊断」App,现在合并进来)
// ==================================================================
private void buildDiagPanel() {
diagPanel = new FrameLayout(this);
diagPanel.setBackgroundColor(C_BG);
LinearLayout column = new LinearLayout(this);
column.setOrientation(LinearLayout.VERTICAL);
column.setPadding(dp(6), dp(6), dp(6), dp(6));
// 顶部:标题 + 操作按钮
LinearLayout top = new LinearLayout(this);
top.setOrientation(LinearLayout.HORIZONTAL);
top.setGravity(Gravity.CENTER_VERTICAL);
TextView title = new TextView(this);
title.setText("设备诊断");
title.setTextColor(C_FG);
title.setTextSize(TypedValue.COMPLEX_UNIT_SP, 15);
top.addView(title, new LinearLayout.LayoutParams(
0, ViewGroup.LayoutParams.WRAP_CONTENT, 1f));
top.addView(smallButton("重新检测", C_ACCENT, new Runnable() {
public void run() { runDiagnostics(); }
}));
top.addView(smallButton("复制全部", C_OK, new Runnable() {
public void run() {
if (diagText == null || diagText.getText().length() == 0) {
toast("还没有报告,先点「重新检测」");
return;
}
copyToWatchClipboard(diagText.getText().toString());
toast("已复制 " + diagText.getText().length() + " 个字符");
}
}));
top.addView(smallButton("返回", C_CARD, new Runnable() {
public void run() { closeDiagPanel(); }
}));
column.addView(top);
diagStatusLine = new TextView(this);
diagStatusLine.setText("点「重新检测」开始(约 8~20 秒)");
diagStatusLine.setTextColor(C_DIM);
diagStatusLine.setTextSize(TypedValue.COMPLEX_UNIT_SP, 11);
diagStatusLine.setPadding(dp(2), dp(4), dp(2), dp(4));
column.addView(diagStatusLine);
// 报告正文
ScrollView scroll = new ScrollView(this);
diagText = new TextView(this);
diagText.setText("还没有报告。\\n\\n点上面的「重新检测」,"
+ "它会真实跑一次局域网自动发现和 TCP 连通测试,"
+ "测完自动复制到剪贴板。");
diagText.setTextColor(C_FG);
diagText.setTextSize(TypedValue.COMPLEX_UNIT_SP, 11);
diagText.setTypeface(android.graphics.Typeface.MONOSPACE);
diagText.setTextIsSelectable(true);
diagText.setPadding(dp(4), dp(4), dp(4), dp(4));
scroll.addView(diagText);
column.addView(scroll, new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, 0, 1f));
// 底部:手动测一个 IP
LinearLayout bottom = new LinearLayout(this);
bottom.setOrientation(LinearLayout.HORIZONTAL);
bottom.setGravity(Gravity.CENTER_VERTICAL);
diagIpInput = new EditText(this);
diagIpInput.setHint("手动测电脑 IP,如 192.168.10.103");
diagIpInput.setHintTextColor(C_DIM);
diagIpInput.setTextColor(C_FG);
diagIpInput.setTextSize(TypedValue.COMPLEX_UNIT_SP, 12);
diagIpInput.setSingleLine(true);
diagIpInput.setInputType(android.text.InputType.TYPE_CLASS_TEXT
| android.text.InputType.TYPE_TEXT_VARIATION_URI);
diagIpInput.setBackground(rounded(C_CARD, 6));
diagIpInput.setPadding(dp(6), dp(6), dp(6), dp(6));
bottom.addView(diagIpInput, new LinearLayout.LayoutParams(
0, ViewGroup.LayoutParams.WRAP_CONTENT, 1f));
bottom.addView(smallButton("测一下", C_ACCENT, new Runnable() {
public void run() { testDiagIp(); }
}));
column.addView(bottom);
diagPanel.addView(column, new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
diagPanel.setVisibility(View.GONE);
}
private void openDiagPanel() {
if (diagPanel == null) {
return;
}
diagPanel.setVisibility(View.VISIBLE);
diagPanel.bringToFront();
if ((diagText.getText() == null || diagText.getText().length() == 0)
&& !diagRunning) {
runDiagnostics();
}
}
private void closeDiagPanel() {
if (diagPanel != null) {
diagPanel.setVisibility(View.GONE);
}
}
private void runDiagnostics() {
if (diagRunning) {
return;
}
diagRunning = true;
diagStatusLine.setText("正在检测…(广播 4 秒 + 必要时扫描整个网段)");
diagText.setText("正在采集,请稍候…\\n\\n先跑一次 UDP 广播;"
+ "如果没结果,会自动扫描整个网段的 TCP 8765。最多约 20 秒。");
io.execute(new Runnable() {
@Override
public void run() {
String report;
try {
report = Diagnostics.build(MainActivity.this,
new Diagnostics.Progress() {
@Override
public void step(final String text) {
runOnUiThread(new Runnable() {
@Override
public void run() {
diagStatusLine.setText(text);
}
});
}
}, true);
} catch (Throwable t) {
report = "诊断执行失败:" + t;
}
final String result = report;
runOnUiThread(new Runnable() {
@Override
public void run() {
diagRunning = false;
diagText.setText(result);
diagStatusLine.setText("检测完成,已自动复制到剪贴板");
copyToWatchClipboard(result);
appendLog("设备诊断完成,已复制到剪贴板");
}
});
}
});
}
private void testDiagIp() {
final String ip = diagIpInput.getText().toString().trim();
if (ip.isEmpty()) {
toast("先填一个 IP");
return;
}
diagStatusLine.setText("正在测试 " + ip + " …");
io.execute(new Runnable() {
@Override
public void run() {
final String extra;
try {
extra = Diagnostics.manualTest(ip);
} catch (Throwable t) {
extra = "测试失败:" + t;
}
runOnUiThread(new Runnable() {
@Override
public void run() {
String current = diagText.getText().toString();
diagText.setText(current + "\\n" + extra + "\\n");
diagStatusLine.setText("测试完成,已追加并复制");
copyToWatchClipboard(diagText.getText().toString());
}
});
}
});
}
'''
def main() -> int:
raw = MAIN.read_bytes()
text = raw.decode("utf-8")
ok = True
def swap(old: str, new: str, tag: str) -> None:
nonlocal text, ok
if old not in text:
print(f" [失败] {tag} 锚点未匹配")
ok = False
return
text = text.replace(old, new, 1)
print(f" {tag}: OK")
swap(FIELDS_OLD, FIELDS_NEW, "字段")
swap(BUILD_CALL_OLD, BUILD_CALL_NEW, "构建调用")
swap(ADD_VIEW_OLD, ADD_VIEW_NEW, "加入 root")
swap(BACK_OLD, BACK_NEW, "返回键处理")
swap(BUTTON_OLD, BUTTON_NEW, "诊断按钮")
if ANCHOR not in text:
print(" [失败] buildMirrorPanel 锚点未匹配")
ok = False
else:
text = text.replace(ANCHOR, DIAG_CODE + ANCHOR, 1)
print(" 诊断面板代码: OK")
if not ok:
return 1
MAIN.write_bytes(text.encode("utf-8"))
print("\n MainActivity.java 已写入")
return 0
if __name__ == "__main__":
sys.exit(main())