# -*- 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())