Files
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

420 lines
18 KiB
Python

# -*- coding: utf-8 -*-
"""删掉「功能」页,把 14 个分组散到各个分页。
新分页(10 个):
媒体 音量 + 媒体
屏幕 显示
文件 文件
语音 静默
电脑 查看 + 工具
网络 网络 + 极客
系统 系统
教室 教室 + 教学
效率 效率
娱乐 娱乐
每个分页 = 原有快捷按钮 + 该分页负责的功能分组(带小标题)。
分组按钮直接从 feature_list 拉取,电脑端加了新功能这里自动就有。
"""
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. TabDef 加 groups ----------------
TABDEF_OLD = ''' private static final class TabDef {
final String name;
final Act[] acts;
TabDef(String name, Act[] acts) {
this.name = name;
this.acts = acts;
}
}'''
TABDEF_NEW = ''' private static final class TabDef {
final String name;
final Act[] acts;
final String[] groups; // 这个分页要渲染哪些功能分组
TabDef(String name, Act[] acts, String... groups) {
this.name = name;
this.acts = acts;
this.groups = groups;
}
}'''
# ---------------- 2. selectTab:快捷按钮 + 分组 ----------------
SELECT_OLD = ''' moveTabIndicator(tabBar.getChildAt(index));
if ("功能".equals(tabDefs[index].name)) {
buildFeatureTab();
return;
}
tabContent.removeAllViews();
cellQueue.clear();
for (Act item : tabDefs[index].acts) {
addRow(tabContent, action(item.label, item.task), null);
}
flushGrid(tabContent);
// 切页时内容淡入 + 轻微上移,做出"流动"的感觉
animateIn(tabContent, 10f, 220);
}'''
SELECT_NEW = ''' moveTabIndicator(tabBar.getChildAt(index));
tabContent.removeAllViews();
cellQueue.clear();
for (Act item : tabDefs[index].acts) {
addRow(tabContent, action(item.label, item.task), null);
}
flushGrid(tabContent);
// 这个分页负责的功能分组
if (tabDefs[index].groups.length > 0) {
renderGroups(tabContent, tabDefs[index].groups);
}
// 切页时内容淡入 + 轻微上移,做出"流动"的感觉
animateIn(tabContent, 10f, 220);
}
/** 把一个分页负责的功能分组渲染成按钮(每组带一个小标题)。 */
private void renderGroups(final LinearLayout container, final String[] groups) {
if (featureCache == null || featureCache.isEmpty()) {
// 还没拉到功能列表:先显示提示,拉到之后重画当前页
TextView hint = new TextView(this);
hint.setText("正在获取电脑端功能…");
hint.setTextColor(C_DIM);
hint.setTextSize(TypedValue.COMPLEX_UNIT_SP, 11);
hint.setPadding(dp(4), dp(6), dp(4), dp(6));
container.addView(hint);
if (featureGroups == null) {
io.execute(new Runnable() {
@Override
public void run() {
try {
Map<String, Object> reply =
client.call(Json.obj("cmd", "feature_list"), 25000);
final List<Map<String, Object>> items = mapList(reply, "features");
ui.post(new Runnable() {
@Override
public void run() {
featureCache = items;
featureGroups = new ArrayList<String>();
for (Map<String, Object> f : items) {
String g = Json.str(f, "group", "其他");
if (!featureGroups.contains(g)) {
featureGroups.add(g);
}
}
selectTab(currentTab);
}
});
} catch (final IOException e) {
ui.post(new Runnable() {
@Override
public void run() {
appendLog("获取功能列表失败:" + e.getMessage());
toast("没连上电脑,功能列表拉不到");
}
});
}
}
});
}
return;
}
for (final String group : groups) {
final List<Map<String, Object>> items =
new ArrayList<Map<String, Object>>();
for (Map<String, Object> f : featureCache) {
if (group.equals(Json.str(f, "group", ""))) {
items.add(f);
}
}
if (items.isEmpty()) {
continue;
}
TextView header = new TextView(this);
header.setText(group);
header.setTextColor(C_ACCENT);
header.setTextSize(TypedValue.COMPLEX_UNIT_SP, 12);
header.setPadding(dp(4), dp(10), dp(4), dp(2));
container.addView(header);
LinearLayout grid = new LinearLayout(this);
grid.setOrientation(LinearLayout.VERTICAL);
container.addView(grid);
cellQueue.clear();
for (final Map<String, Object> item : items) {
addRow(grid, action(Json.str(item, "label", "?"), new Runnable() {
@Override
public void run() {
dispatchFeature(item);
}
}), null);
}
flushGrid(grid);
}
}'''
# ---------------- 3. initTabs:新分页表 ----------------
INIT_ANCHOR_START = " tabDefs = new TabDef[]{"
INIT_ANCHOR_END = " tabBar.removeAllViews();"
def main() -> int:
text = MAIN.read_text(encoding="utf-8")
for old, new, tag in ((TABDEF_OLD, TABDEF_NEW, "TabDef 加 groups"),
(SELECT_OLD, SELECT_NEW, "selectTab + renderGroups")):
if old not in text:
print(f" [失败] {tag} —— 锚点没找到")
return 1
text = text.replace(old, new, 1)
print(f" {tag}: OK")
# 替换整个 tabDefs 数组
start = text.find(INIT_ANCHOR_START)
end = text.find(INIT_ANCHOR_END, start)
if start < 0 or end < 0:
print(f" [失败] 找不到 tabDefs 数组 (start={start}, end={end})")
return 1
# 保留数组里原有的 Act 定义不现实(太散),改为按名字重建:
# 逐个分页用「快捷按钮 + 分组」表达
new_tabs = ''' tabDefs = new TabDef[]{
new TabDef("媒体", new Act[]{
act("播放/暂停", new Runnable() {
public void run() { media("playpause"); }
}),
act("停止", new Runnable() {
public void run() { media("stop"); }
}),
act("上一曲", new Runnable() {
public void run() { media("prev"); }
}),
act("下一曲", new Runnable() {
public void run() { media("next"); }
}),
act("音量 +", new Runnable() {
public void run() { media("volup"); }
}),
act("音量 -", new Runnable() {
public void run() { media("voldown"); }
}),
act("静音", new Runnable() {
public void run() { media("mute"); }
}),
}, "音量", "媒体"),
new TabDef("屏幕", new Act[]{
act("屏幕镜像", new Runnable() {
public void run() { openMirrorPanel(); }
}),
act("截屏", new Runnable() {
public void run() { doScreenshot(); }
}),
act("投图到电脑", new Runnable() {
public void run() { pickFile(REQ_IMAGE, "image/*"); }
}),
act("放音到电脑", new Runnable() {
public void run() { pickFile(REQ_AUDIO, "audio/*"); }
}),
}, "显示"),
new TabDef("文件", new Act[]{
act("发送文件", new Runnable() {
public void run() { pickFile(REQ_FILE, "*/*"); }
}),
act("接收文件", new Runnable() {
public void run() { showRemoteFiles(); }
}),
act("剪贴板→电脑", new Runnable() {
public void run() { promptClipboardToPc(); }
}),
act("取电脑剪贴板", new Runnable() {
public void run() { fetchClipboard(); }
}),
}, "文件"),
new TabDef("语音", new Act[]{
act("语音播报", new Runnable() {
public void run() { promptSay(); }
}),
act("音色", new Runnable() {
public void run() { pickVoice(); }
}),
act("朗读剪贴板", new Runnable() {
public void run() {
sendCommand(Json.obj("cmd", "say_clip"), "朗读剪贴板");
}
}),
act("电脑音乐", new Runnable() {
public void run() { pickLocalAudio(); }
}),
act("弹通知", new Runnable() {
public void run() { promptNotify(); }
}),
}, "静默"),
new TabDef("电脑", new Act[]{
act("快捷命令", new Runnable() {
public void run() { pickQuickApp(); }
}),
act("打开网址", new Runnable() {
public void run() { promptOpenUrl(); }
}),
act("进程管理", new Runnable() {
public void run() { pickProcess(); }
}),
act("性能", new Runnable() {
public void run() { showPerf(); }
}),
act("磁盘", new Runnable() {
public void run() { showDisk(); }
}),
act("电量", new Runnable() {
public void run() { showBattery(); }
}),
}, "查看", "工具"),
new TabDef("网络", new Act[]{
act("本机监听端口", new Runnable() {
public void run() {
sendCommand(Json.obj("cmd", "feature_run",
"name", "open_ports",
"params", Json.obj()), "监听端口");
}
}),
act("确认能连上", new Runnable() {
public void run() { requestPcSelfCheck(); }
}),
}, "网络", "极客"),
new TabDef("系统", new Act[]{
act("锁屏", new Runnable() {
public void run() { confirmPower("lock", "锁屏"); }
}),
act("睡眠", new Runnable() {
public void run() { confirmPower("sleep", "睡眠"); }
}),
act("关机", new Runnable() {
public void run() { confirmPower("shutdown", "关机"); }
}),
act("重启", new Runnable() {
public void run() { confirmPower("restart", "重启"); }
}),
act("取消定时关机", new Runnable() {
public void run() { cancelShutdown(); }
}),
act("定时关机", new Runnable() {
public void run() { pickShutdownTimer(); }
}),
}, "系统"),
new TabDef("教室", new Act[]{
act("屏蔽游戏网站", new Runnable() {
public void run() {
sendCommand(Json.obj("cmd", "feature_run",
"name", "game_block_common",
"params", Json.obj()), "屏蔽游戏网站");
}
}),
act("结束游戏进程", new Runnable() {
public void run() {
sendCommand(Json.obj("cmd", "feature_run",
"name", "kill_games",
"params", Json.obj()), "结束游戏");
}
}),
act("一键恢复全部", new Runnable() {
public void run() {
sendCommand(Json.obj("cmd", "feature_run",
"name", "restore_all",
"params", Json.obj()), "恢复全部限制");
}
}),
act("看有哪些限制", new Runnable() {
public void run() {
sendCommand(Json.obj("cmd", "feature_run",
"name", "restrict_status",
"params", Json.obj()), "限制状态");
}
}),
}, "教室", "教学"),
new TabDef("效率", new Act[]{
act("加待办", new Runnable() {
public void run() {
promptTwo("加一条待办", "内容", "(第二格留空)",
new TwoHandler() {
public void onInput(String a, String b) {
runFeature("todo_add",
Json.obj("text", a), "待办");
}
});
}
}),
act("看所有待办", new Runnable() {
public void run() {
sendCommand(Json.obj("cmd", "feature_run",
"name", "todo_list",
"params", Json.obj()), "待办列表");
}
}),
}, "效率"),
new TabDef("娱乐", new Act[]{
act("掷骰子", new Runnable() {
public void run() {
sendCommand(Json.obj("cmd", "feature_run",
"name", "dice",
"params", Json.obj()), "掷骰子");
}
}),
act("抽奖", new Runnable() {
public void run() {
sendCommand(Json.obj("cmd", "feature_run",
"name", "lottery",
"params", Json.obj()), "抽奖");
}),
act("抛硬币", new Runnable() {
public void run() {
sendCommand(Json.obj("cmd", "feature_run",
"name", "coin_flip",
"params", Json.obj()), "抛硬币");
}),
}, "娱乐"),
};
'''
text = text[:start] + new_tabs + text[end:]
print(" initTabs: 分页表已重建(功能页已删除)")
# 删掉旧的 buildFeatureTab(已经没人用了)
bf_start = text.find(" /** 「功能」页:分组由电脑端声明,这里拉取后动态生成按钮。 */")
if bf_start > 0:
# 找到下一个同级方法
bf_end = text.find("\n /** 指示器滑到目标标签", bf_start)
if bf_end < 0:
bf_end = text.find("\n private ", bf_start + 10)
if bf_end > bf_start:
text = text[:bf_start] + text[bf_end + 1:]
print(" 旧 buildFeatureTab 已删除")
MAIN.write_text(text, encoding="utf-8")
return 0
if __name__ == "__main__":
sys.exit(main())