Files
ykxmz/_build/patch_watch_tabs.py
T
xmz7891 b705216e4a ykxmz 1.5.0锛氫慨澶嶆墜琛ㄧ TLS 璇佷功鏍¢獙 + MTU 淇缃戠粶绋冲畾鎬?
琚帶绔?- 淇 MTU 闂锛氬す MSS + 璺緞 MTU 鎺㈡祴锛堝叕缃戝彲鐢ㄧ巼 48% -> 100%锛?- 澶嶇敤 HTTP 杩炴帴锛堝厤鎺夋瘡娆℃柊寤?TLS 鐨?30% 鎻℃墜澶辫触锛?- 姣忓垎閽熷洖澶存壘灞€鍩熺綉锛岃嚜鍔ㄥ垏鍥炲揩 400 鍊嶇殑璺緞
- 鐢婚潰蹇冭烦甯э細闈欐鏃剁渷娴侀噺锛屽鎴风浠嶇煡閬撴祦鏄椿鐨?- 閿佸睆鏃剁粰鍑哄彲鎿嶄綔鐨勬彁绀?
鎵嬭〃绔?- 鍐呯疆 GTS Root R4 / GlobalSign Root CA锛屼慨 SSLHandshakeException
- 杩炴帴閿欒缈昏瘧鎴愪汉璇濓紙璇佷功/鏃堕棿/瑙f瀽/瓒呮椂鍒嗗埆鎻愮ず锛?- 缁忎腑杞繛涓婂悗鐢ㄦ湇鍔″櫒鎶ョ殑鍦板潃鍒锋柊宸查厤瀵硅褰?- 鏂板灞€鍩熺綉杩炴帴椤甸潰銆佹湇鍔″櫒寤惰繜妫€娴?
鏈嶅姟鍣ㄧ
- 鏀寔 chunked 璇锋眰浣擄紙淇瘡 16 绉掍竴娆$殑杩炴帴閿欎贡涓庡ぇ闈㈢Н 502锛?- 缃戠粶璋冧紭鑴氭湰 net-tune.sh锛堝紑鏈鸿嚜鍔ㄥ簲鐢級
- 绠$悊椤碉細鎵归噺鍒犻櫎銆佺綉缁滀綋妫€銆佷笅杞藉尯
- 闅ч亾鏀?HTTP/2 + 鍙岃繛鎺?
鏂囨。
- 璁稿彲鍗忚 / 瀹夎鍓嶅繀璇?/ 瀹夎鍚庨』鐭ワ紙浣滆€咃細灏忛害瀛愶級
2026-10-06 19:01:05 +08:00

560 lines
23 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# -*- coding: utf-8 -*-
"""手表端 UI 彻底重做:分页 + 37 个功能。
原来是一长条 28 个按钮的网格,要滚 274dp。
现在分成 6 页,每页最多 7 个按钮 —— **完全不用滚动**。
媒体 (7) 屏幕 (6) 文件 (6) 语音 (5) 电脑 (6) 系统 (7)
"""
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")
START = " // 按钮网格\n"
END = " flushGrid(grid); // 按当前列数把上面入队的按钮切成行\n"
NEW_PANEL = ''' // ---- 分页标签 ----
HorizontalScrollView tabScroll = new HorizontalScrollView(this);
tabScroll.setHorizontalScrollBarEnabled(false);
tabBar = new LinearLayout(this);
tabBar.setOrientation(LinearLayout.HORIZONTAL);
tabBar.setPadding(0, 0, 0, 0);
tabScroll.addView(tabBar);
controlPanel.addView(tabScroll, new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, dp(42)));
// ---- 分页内容 ----
tabContent = new LinearLayout(this);
tabContent.setOrientation(LinearLayout.VERTICAL);
tabContent.setPadding(0, dp(6), 0, dp(6));
ScrollView contentScroll = new ScrollView(this);
contentScroll.addView(tabContent, new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT));
controlPanel.addView(contentScroll, new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, 0, 1f));
'''
# ---------------------------------------------------------------- tabs 机制
TAB_METHODS_ANCHOR = ''' private Button bigButton(String text, int color, float sizeSp) {'''
TAB_METHODS = ''' // ==================================================================
// 分页
// ==================================================================
private static final class Act {
final String label;
final Runnable task;
Act(String label, Runnable task) {
this.label = label;
this.task = task;
}
}
private static final class TabDef {
final String name;
final Act[] acts;
TabDef(String name, Act[] acts) {
this.name = name;
this.acts = acts;
}
}
private TabDef[] tabDefs;
private LinearLayout tabBar;
private LinearLayout tabContent;
private int currentTab = 0;
private Act act(String label, Runnable task) {
return new Act(label, task);
}
/** 定义所有分页。每页最多 7 个按钮,一屏放得下,不用滚。 */
private void initTabs() {
tabDefs = new TabDef[]{
new TabDef("媒体", new Act[]{
act("播放/暂停", new Runnable() {
public void run() { media("play_pause"); }
}),
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("vol_up"); }
}),
act("音量 -", new Runnable() {
public void run() { media("vol_down"); }
}),
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/*"); }
}),
act("关显示器", new Runnable() {
public void run() { confirmMonitor(); }
}),
act("设为壁纸", new Runnable() {
public void run() { confirmWallpaper(); }
}),
}),
new TabDef("文件", new Act[]{
act("发送文件", new Runnable() {
public void run() { pickFile(REQ_FILE, "*/*"); }
}),
act("接收文件", new Runnable() {
public void run() { showReceivedFiles(); }
}),
act("剪贴板→电脑", new Runnable() {
public void run() { promptClipSet(); }
}),
act("取电脑剪贴板", new Runnable() {
public void run() { fetchClip(); }
}),
act("剪贴板历史", new Runnable() {
public void run() { pickClipHistory(); }
}),
act("打开文件夹", new Runnable() {
public void run() { pickFolder(); }
}),
}),
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() { 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("reboot", "重启"); }
}),
act("取消定时关机", new Runnable() {
public void run() { cancelShutdown(); }
}),
act("定时关机", new Runnable() {
public void run() { pickShutdownTimer(); }
}),
act("设备诊断", new Runnable() {
public void run() { openDiagPanel(); }
}),
}),
};
tabBar.removeAllViews();
for (int i = 0; i < tabDefs.length; i++) {
final int index = i;
TextView tab = new TextView(this);
tab.setText(tabDefs[i].name);
tab.setTextSize(TypedValue.COMPLEX_UNIT_SP, 13);
tab.setGravity(Gravity.CENTER);
tab.setPadding(dp(14), dp(8), dp(14), dp(8));
tab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
selectTab(index);
}
});
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.MATCH_PARENT);
params.setMargins(0, 0, dp(6), 0);
tab.setLayoutParams(params);
tabBar.addView(tab);
}
selectTab(0);
}
private void selectTab(int index) {
if (tabDefs == null || index < 0 || index >= tabDefs.length) {
return;
}
currentTab = index;
for (int i = 0; i < tabBar.getChildCount(); i++) {
View child = tabBar.getChildAt(i);
boolean on = (i == index);
child.setBackground(rounded(on ? C_ACCENT : C_CARD, 8));
if (child instanceof TextView) {
((TextView) child).setTextColor(on ? 0xFFFFFFFF : C_FG);
}
}
tabContent.removeAllViews();
cellQueue.clear();
for (Act item : tabDefs[index].acts) {
addRow(tabContent, action(item.label, item.task), null);
}
flushGrid(tabContent);
}
// ==================================================================
// 新功能
// ==================================================================
private void media(String what) {
sendCommand(Json.obj("cmd", "media", "action", what), "媒体");
}
private void confirmMonitor() {
new AlertDialog.Builder(this)
.setTitle("关闭显示器")
.setMessage("要关掉电脑显示器吗?(动一下鼠标或按键就能唤醒)")
.setPositiveButton("关掉", new android.content.DialogInterface.OnClickListener() {
@Override
public void onClick(android.content.DialogInterface d, int w) {
sendCommand(Json.obj("cmd", "monitor", "on", false), "关显示器");
}
})
.setNegativeButton("取消", null)
.show();
}
private void confirmWallpaper() {
new AlertDialog.Builder(this)
.setTitle("设为壁纸")
.setMessage("把电脑上最近收到的那张图片设为桌面壁纸?")
.setPositiveButton("设置", new android.content.DialogInterface.OnClickListener() {
@Override
public void onClick(android.content.DialogInterface d, int w) {
sendCommand(Json.obj("cmd", "set_wallpaper"), "设置壁纸");
}
})
.setNegativeButton("取消", null)
.show();
}
private void pickShutdownTimer() {
final int[] minutes = {15, 30, 60, 120};
final String[] labels = {"15 分钟后", "30 分钟后", "1 小时后", "2 小时后"};
showChooser("定时关机", new ArrayList<String>(java.util.Arrays.asList(labels)),
new Chooser() {
@Override
public void onPick(int index) {
sendCommand(Json.obj("cmd", "shutdown_timer",
"minutes", minutes[index]), "定时关机");
}
});
}
private void cancelShutdown() {
sendCommand(Json.obj("cmd", "shutdown_timer", "cancel", true), "取消定时关机");
}
private void promptOpenUrl() {
promptInput("在电脑上打开网址", "如 baidu.com", "https://", new InputHandler() {
@Override
public void onInput(String text) {
if (!text.isEmpty()) {
sendCommand(Json.obj("cmd", "open_url", "url", text), "打开网址");
}
}
});
}
private void pickQuickApp() {
final String[][] apps = {
{"任务管理器", "taskmgr"},
{"资源管理器", "explorer"},
{"命令提示符", "cmd"},
{"计算器", "calc"},
{"记事本", "notepad"},
{"画图", "mspaint"},
{"控制面板", "control"},
{"设备管理器", "devmgmt"},
};
List<String> labels = new ArrayList<String>();
for (String[] item : apps) {
labels.add(item[0]);
}
showChooser("在电脑上打开", labels, new Chooser() {
@Override
public void onPick(int index) {
sendCommand(Json.obj("cmd", "quick", "app", apps[index][1]),
"打开" + apps[index][0]);
}
});
}
private void pickFolder() {
final String[][] folders = {
{"下载", "downloads"},
{"文档", "documents"},
{"桌面", "desktop"},
{"图片", "pictures"},
{"音乐", "music"},
{"接收目录", "received"},
{"程序目录", "app"},
};
List<String> labels = new ArrayList<String>();
for (String[] item : folders) {
labels.add(item[0]);
}
showChooser("打开电脑文件夹", labels, new Chooser() {
@Override
public void onPick(int index) {
sendCommand(Json.obj("cmd", "open_folder", "folder", folders[index][1]),
"打开" + folders[index][0]);
}
});
}
private void pickProcess() {
appendLog("正在获取电脑进程…");
io.execute(new Runnable() {
@Override
public void run() {
try {
final Map<String, Object> reply =
client.call(Json.obj("cmd", "proc_list"), 20000);
final List<String> names = stringList(reply, "names");
final List<String> pids = stringList(reply, "pids");
ui.post(new Runnable() {
@Override
public void run() {
if (names.isEmpty()) {
toast("没读到进程,或没连上");
return;
}
showChooser("占内存最多的进程", names, new Chooser() {
@Override
public void onPick(int index) {
confirmKill(names.get(index), pids.get(index));
}
});
}
});
} catch (final IOException e) {
postError("获取进程", e);
}
}
});
}
private void confirmKill(String label, String pid) {
final int target;
try {
target = Integer.parseInt(pid);
} catch (NumberFormatException e) {
toast("进程号不对");
return;
}
new AlertDialog.Builder(this)
.setTitle("结束进程")
.setMessage("确定要结束这个进程吗?\n\n" + label)
.setPositiveButton("结束", new android.content.DialogInterface.OnClickListener() {
@Override
public void onClick(android.content.DialogInterface d, int w) {
sendCommand(Json.obj("cmd", "proc_kill", "pid", target), "结束进程");
}
})
.setNegativeButton("取消", null)
.show();
}
private void showPerf() {
fetchAndShow("性能", Json.obj("cmd", "perf"), "note");
}
private void showDisk() {
fetchAndShow("磁盘占用", Json.obj("cmd", "disk"), "note");
}
private void showBattery() {
fetchAndShow("电脑电量", Json.obj("cmd", "battery"), "note");
}
/** 拉一条命令,把 note 用对话框显示出来。 */
private void fetchAndShow(final String title, final Map<String, Object> request,
final String field) {
appendLog("正在获取" + title + "…");
io.execute(new Runnable() {
@Override
public void run() {
try {
final Map<String, Object> reply = client.call(request, 15000);
final boolean ok = Json.bool(reply, "ok", false);
final String note = ok
? Json.str(reply, field, "(无内容)")
: Json.str(reply, "error", "读取失败");
ui.post(new Runnable() {
@Override
public void run() {
appendLog(title + ":" + note);
new AlertDialog.Builder(MainActivity.this)
.setTitle(title)
.setMessage(note)
.setPositiveButton("知道了", null)
.show();
}
});
} catch (final IOException e) {
postError(title, e);
}
}
});
}
private void pickClipHistory() {
appendLog("正在获取剪贴板历史…");
io.execute(new Runnable() {
@Override
public void run() {
try {
final Map<String, Object> reply =
client.call(Json.obj("cmd", "clip_history"), 12000);
final List<String> texts = stringList(reply, "texts");
final List<String> times = stringList(reply, "times");
ui.post(new Runnable() {
@Override
public void run() {
if (texts.isEmpty()) {
toast("电脑剪贴板还没有历史");
return;
}
List<String> labels = new ArrayList<String>();
for (int i = 0; i < texts.size(); i++) {
String preview = texts.get(i).replace("\\n", " ");
if (preview.length() > 22) {
preview = preview.substring(0, 22) + "…";
}
String when = i < times.size() ? times.get(i) + " " : "";
labels.add(when + preview);
}
showChooser("剪贴板历史(点一条取到手表)", labels, new Chooser() {
@Override
public void onPick(int index) {
copyToWatchClipboard(texts.get(index));
toast("已复制到手表剪贴板");
appendLog("取回一条剪贴板历史");
}
});
}
});
} catch (final IOException e) {
postError("剪贴板历史", e);
}
}
});
}
'''
def main() -> int:
text = MAIN.read_text(encoding="utf-8")
start = text.find(START)
end = text.find(END)
if start < 0 or end < 0:
print(f" [失败] 找不到网格区块 (start={start}, end={end})")
return 1
end += len(END)
text = text[:start] + NEW_PANEL + text[end:]
print(" 分页面板: OK")
if TAB_METHODS_ANCHOR not in text:
print(" [失败] bigButton 锚点未匹配")
return 1
text = text.replace(TAB_METHODS_ANCHOR, TAB_METHODS + TAB_METHODS_ANCHOR, 1)
print(" 分页机制 + 新功能: OK")
# addRow 要能接受 null(一页里按钮数可能是奇数)
old_add = ''' private void addRow(LinearLayout parent, View left, View right) {
// 只入队,真正的行切分在 flushGrid() 里按 gridColumns 统一做。
// 这样上层那串 addRow(a, b) 不用改,列数就能自适应。
cellQueue.add(left);
cellQueue.add(right);
}'''
new_add = ''' private void addRow(LinearLayout parent, View left, View right) {
// 只入队,真正的行切分在 flushGrid() 里按 gridColumns 统一做。
// 允许传 null:分页里某一页的按钮数可能是奇数。
if (left != null) {
cellQueue.add(left);
}
if (right != null) {
cellQueue.add(right);
}
}'''
if old_add in text:
text = text.replace(old_add, new_add, 1)
print(" addRow 支持 null: OK")
else:
print(" [警告] addRow 未替换")
# 初始化分页
old_init = " showConnectPanel();"
new_init = " initTabs();\n showConnectPanel();"
if old_init in text:
text = text.replace(old_init, new_init, 1)
print(" initTabs 调用: OK")
MAIN.write_text(text, encoding="utf-8")
return 0
if __name__ == "__main__":
sys.exit(main())