mirror of
https://gitee.com/xmz7891/ykxmz.git
synced 2026-10-18 18:20:43 +00:00
== 用户要求 ==
"安卓那个逻辑也要改一下,就是你输入配对码后,进入一个页面,
页面有两个按钮,第一个按钮是远控,第二个按钮是静默更新被控端"
== 做成什么样 ==
新增 choicePanel,连接成功后先进这一页:
已连接「电脑名」
电脑端 2.1.5 → 可更新到 2.2.0
[ 远控 ] <- 第一个按钮
[ 静默更新被控端 ] <- 第二个按钮
按钮顺序按用户说的来(远控在前)。
页面顶上显示电脑名和**电脑端当前版本** —— 用户之前要求
"手表端连接那个电脑端也可以查看电脑端目前所处的版本",
这里是最自然的展示位置(不弹框,只显示一行)。
为什么不让它直接进控制页:老版本的被控端可能缺功能或有 bug,
"先更新再远控"是更合理的顺序 —— 但不强制,给用户自己选。
== 一个刻意不改的地方 ==
自动切到中转服务器**恢复**时的 `showControlPanel()` **没动** ——
用户本来就在远控页,中途弹一层选择页会打断他。
测试里专门断言了这一点,免得以后有人"顺手统一一下"改坏。
== 顺手修的几个问题 ==
1. **忘了定义 lastDeviceName** —— 写选择页时凭直觉用了这个变量,
编译报"找不到符号"。补上字段并在连接回调里赋值。
(教训:写代码时要用的变量,先确认它存在。)
2. **泄漏闸门拦下我自己** —— 这两天写的一批服务器诊断/配置脚本里
硬编码了服务器 IP(26 处,5 个文件)。规则是 IP 不能进任何
被跟踪的文件。已全部改成占位符,真实值仍从
_build/.lightsail_host(已 gitignore)读。
3. **verify_all.ps1 删掉「锁屏时记 SKIP」的逻辑** ——
用户明确要求删。那个好意带来一个更糟的后果:锁屏时几关根本不跑,
回归"全绿"了但关键测试其实一跳而过。
**"因为环境不满足所以不测"比"测了发现失败"更危险。**
删掉之后果然暴露出 3 个 FAIL —— 屏幕解锁后重跑全部通过,
确认是环境问题而不是代码问题。
== 服务器这边(不在这份提交里,但同一天做的)==
* nginx 按用户要求重排:
IP(80) 任何路径 -> 404(没东西)
域名 / -> 官网;/zhongzhuanzhan -> 中转站设置
9100 -> 同样的内容(等 AWS 开防火墙)
* 删掉早年 Cloudflare Tunnel 的 systemd 残留
* **补上 ACME 挑战例外** —— 这是最要紧的一条:
80 端口原来把所有请求都 301 到 https,包括
/.well-known/acme-challenge/,**续期时会失败**。
而且它是延迟爆发的(最长 90 天后证书到期才暴露)。
* 清理 1Panel 安装包残留(223MB)
== 验证 ==
全量回归 21 关全部 PASS(零 FAIL,桌面可用状态)
Java 编译通过(android-34, --release 8)
623+ Python 文件语法通过
泄漏闸门通过
全部产物重建,Win7 兼容校验通过
324 lines
13 KiB
Python
324 lines
13 KiB
Python
# -*- coding: utf-8 -*-
|
||
r'''手表端:输入配对码连上之后,先到一个「选择页」。
|
||
|
||
== 用户要求(原话)==
|
||
|
||
"安卓那个逻辑也要改一下,就是你输入配对码后,进入一个页面,
|
||
页面有两个按钮,第一个按钮是远控,第二个按钮是静默更新被控端"
|
||
|
||
== 现状 ==
|
||
|
||
连接成功后直接 `showControlPanel()`(进控制页)。用户根本没机会
|
||
先升级被控端 —— 而老版本的被控端可能缺功能、有 bug,
|
||
"先更新再远控"其实是更合理的顺序。
|
||
|
||
== 改法 ==
|
||
|
||
新增一个 `choicePanel`:
|
||
|
||
┌──────────────────────────┐
|
||
│ 已连接「电脑名」 │
|
||
│ 电脑端版本 2.1.5 │ <- 一眼看到要不要更新
|
||
│ │
|
||
│ ┌────────────────────┐ │
|
||
│ │ 远控 │ │ <- 第一个按钮
|
||
│ └────────────────────┘ │
|
||
│ ┌────────────────────┐ │
|
||
│ │ 静默更新被控端 │ │ <- 第二个按钮
|
||
│ └────────────────────┘ │
|
||
└──────────────────────────┘
|
||
|
||
**按钮顺序按用户说的来**:远控在前(主操作),更新在后。
|
||
|
||
之后:
|
||
|
||
* 连接成功(`showControlPanel()` 那个调用点)改成 `showChoicePanel()`
|
||
* **自动切中转恢复**那个调用点**不改** —— 用户本来就在远控页,
|
||
中途弹一个选择页会打断他
|
||
* 「静默更新被控端」复用已经做好的 `checkPcUpdate()`
|
||
(查版本 -> 确认框 -> 触发更新)
|
||
* 版本号显示在这一页上:用户说"手表端连接那个电脑端也可以查看
|
||
电脑端目前所处的版本",这里是最自然的展示位置
|
||
|
||
== 一些细节 ==
|
||
|
||
* 顶部快捷栏在这一页照样在(用户要求"不管干什么上面都有一栏")
|
||
* 面板要加进 `showOnly` 的数组,否则它会永远压在最上面
|
||
* 也要 `applyPanelTopMargin`,否则顶部被快捷栏盖住
|
||
|
||
* **版本号是异步查的**:连上后立刻查一次;查回来之前显示"正在查…"。
|
||
不查的话用户不知道要不要点更新。
|
||
'''
|
||
from __future__ import annotations
|
||
|
||
import sys
|
||
from pathlib import Path
|
||
|
||
ROOT = Path(__file__).resolve().parent.parent
|
||
MAIN = ROOT / "android" / "src" / "com" / "lanremote" / "watch" / "MainActivity.java"
|
||
|
||
# ---- 1. 字段 ----
|
||
F_ANCHOR = " private FrameLayout mousePanel;"
|
||
F_NEW = ''' private FrameLayout mousePanel;
|
||
|
||
/**
|
||
* 连上之后先给用户看的那一页:远控 / 静默更新被控端。
|
||
*
|
||
* <p>用户要求:"输入配对码后,进入一个页面,页面有两个按钮,
|
||
* 第一个按钮是远控,第二个按钮是静默更新被控端"。
|
||
*
|
||
* <p>为什么不让它直接进控制页:老版本的被控端可能缺功能或者有 bug,
|
||
* "先更新再远控"是更合理的顺序 —— 但也不能强制,所以给用户选。
|
||
*/
|
||
private FrameLayout choicePanel;
|
||
/** 选择页上显示"电脑端版本 X.Y.Z"那一行。 */
|
||
private TextView choiceVersionText;
|
||
/** 选择页上显示"已连接 XXX"。 */
|
||
private TextView choiceWhoText;'''
|
||
|
||
# ---- 2. 建面板的方法,插在 showOnly 之前 ----
|
||
M_ANCHOR = " private void showOnly(View keep) {"
|
||
|
||
M_NEW = ''' // ==================================================================
|
||
// 连接后的选择页(远控 / 静默更新)
|
||
// ==================================================================
|
||
|
||
/**
|
||
* 建选择页。
|
||
*
|
||
* <p>布局从上到下:连接信息 -> 版本 -> [远控] -> [静默更新被控端]。
|
||
* 按钮顺序就是用户说的顺序(远控在前)。
|
||
*/
|
||
private void buildChoicePanel() {
|
||
choicePanel = new FrameLayout(this);
|
||
choicePanel.setBackgroundColor(C_BG);
|
||
|
||
LinearLayout box = new LinearLayout(this);
|
||
box.setOrientation(LinearLayout.VERTICAL);
|
||
box.setPadding(dp(14), dp(20), dp(14), dp(14));
|
||
box.setGravity(android.view.Gravity.CENTER_HORIZONTAL);
|
||
choicePanel.addView(box, new FrameLayout.LayoutParams(
|
||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||
ViewGroup.LayoutParams.MATCH_PARENT));
|
||
|
||
choiceWhoText = new TextView(this);
|
||
choiceWhoText.setText("已连接");
|
||
choiceWhoText.setTextColor(C_FG);
|
||
choiceWhoText.setTextSize(14f);
|
||
choiceWhoText.setGravity(android.view.Gravity.CENTER);
|
||
box.addView(choiceWhoText);
|
||
|
||
choiceVersionText = new TextView(this);
|
||
choiceVersionText.setText("电脑端版本:正在查…");
|
||
choiceVersionText.setTextColor(C_DIM);
|
||
choiceVersionText.setTextSize(12f);
|
||
choiceVersionText.setGravity(android.view.Gravity.CENTER);
|
||
choiceVersionText.setPadding(0, dp(4), 0, dp(10));
|
||
box.addView(choiceVersionText);
|
||
|
||
// 第一个按钮:远控
|
||
Button ctrl = bigButton("远控", C_OK, 17);
|
||
LinearLayout.LayoutParams ctrlLp = new LinearLayout.LayoutParams(
|
||
ViewGroup.LayoutParams.MATCH_PARENT, dp(54));
|
||
ctrlLp.setMargins(0, 0, 0, dp(10));
|
||
ctrl.setLayoutParams(ctrlLp);
|
||
ctrl.setOnClickListener(new View.OnClickListener() {
|
||
@Override
|
||
public void onClick(View v) {
|
||
showControlPanel();
|
||
}
|
||
});
|
||
box.addView(ctrl);
|
||
|
||
// 第二个按钮:静默更新被控端
|
||
Button upd = bigButton("静默更新被控端", C_ACCENT, 15);
|
||
LinearLayout.LayoutParams updLp = new LinearLayout.LayoutParams(
|
||
ViewGroup.LayoutParams.MATCH_PARENT, dp(54));
|
||
upd.setLayoutParams(updLp);
|
||
upd.setOnClickListener(new View.OnClickListener() {
|
||
@Override
|
||
public void onClick(View v) {
|
||
// 复用已经做好的那条链路:查版本 -> 确认 -> 触发更新
|
||
checkPcUpdate();
|
||
}
|
||
});
|
||
box.addView(upd);
|
||
|
||
TextView tip = new TextView(this);
|
||
tip.setText("更新时电脑会自动下载并静默安装,"
|
||
+ "不会弹任何窗口,设置和配对码都不变。");
|
||
tip.setTextColor(C_DIM);
|
||
tip.setTextSize(10f);
|
||
tip.setGravity(android.view.Gravity.CENTER);
|
||
tip.setPadding(0, dp(10), 0, 0);
|
||
box.addView(tip);
|
||
}
|
||
|
||
/** 显示选择页,并顺手查一下电脑端版本。 */
|
||
private void showChoicePanel() {
|
||
if (choicePanel == null) {
|
||
buildChoicePanel();
|
||
}
|
||
if (choiceWhoText != null) {
|
||
String who = lastDeviceName;
|
||
choiceWhoText.setText(who == null || who.isEmpty()
|
||
? "已连接" : "已连接「" + who + "」");
|
||
}
|
||
if (choiceVersionText != null) {
|
||
choiceVersionText.setText("电脑端版本:正在查…");
|
||
}
|
||
showOnly(choicePanel);
|
||
queryPcVersionForChoice();
|
||
}
|
||
|
||
/**
|
||
* 查电脑端版本填到选择页上。
|
||
*
|
||
* <p>单独写一个(不直接用 checkPcUpdate)是因为那个会弹确认框 ——
|
||
* 这里只是"让你看一眼版本",不该弹东西。
|
||
*/
|
||
private void queryPcVersionForChoice() {
|
||
io.execute(new Runnable() {
|
||
@Override
|
||
public void run() {
|
||
Map<String, Object> reply;
|
||
try {
|
||
reply = client.call(Json.obj("cmd", "update",
|
||
"action", "check"), 25000);
|
||
} catch (final Throwable error) {
|
||
reply = null;
|
||
}
|
||
final String text;
|
||
if (reply != null && Boolean.TRUE.equals(reply.get("ok"))) {
|
||
String cur = String.valueOf(reply.get("current"));
|
||
String latest = String.valueOf(reply.get("latest"));
|
||
text = Boolean.TRUE.equals(reply.get("needUpdate"))
|
||
? "电脑端 " + cur + " → 可更新到 " + latest
|
||
: "电脑端 " + cur + "(已是最新)";
|
||
} else {
|
||
text = "电脑端版本:查不到(可以点下面的更新按钮再试)";
|
||
}
|
||
ui.post(new Runnable() {
|
||
@Override
|
||
public void run() {
|
||
if (choiceVersionText != null) {
|
||
choiceVersionText.setText(text);
|
||
}
|
||
}
|
||
});
|
||
}
|
||
});
|
||
}
|
||
|
||
'''
|
||
|
||
# ---- 3. showOnly 数组 ----
|
||
O_SO = ''' View[] all = {connectPanel, controlPanel, mirrorPanel, lanPanel,
|
||
settingsPanel, mousePanel};'''
|
||
N_SO = ''' View[] all = {connectPanel, controlPanel, mirrorPanel, lanPanel,
|
||
settingsPanel, mousePanel, choicePanel};'''
|
||
|
||
# ---- 4. onCreate:建 + 挂 + 让位 ----
|
||
O_OC = ''' loadQuickSlots();
|
||
buildMousePanel(); // 只建,先不显示(showOnly 会切)
|
||
buildQuickBar();'''
|
||
N_OC = ''' loadQuickSlots();
|
||
buildMousePanel(); // 只建,先不显示(showOnly 会切)
|
||
buildChoicePanel(); // 同上:连上之后才显示
|
||
buildQuickBar();'''
|
||
|
||
O_OC2 = ''' applyPanelTopMargin(mousePanel);'''
|
||
N_OC2 = ''' applyPanelTopMargin(mousePanel);
|
||
applyPanelTopMargin(choicePanel);'''
|
||
|
||
O_OC3 = ''' root.addView(mousePanel, new FrameLayout.LayoutParams(
|
||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||
ViewGroup.LayoutParams.MATCH_PARENT));
|
||
mousePanel.setVisibility(View.GONE);'''
|
||
N_OC3 = ''' root.addView(mousePanel, new FrameLayout.LayoutParams(
|
||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||
ViewGroup.LayoutParams.MATCH_PARENT));
|
||
mousePanel.setVisibility(View.GONE);
|
||
root.addView(choicePanel, new FrameLayout.LayoutParams(
|
||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||
ViewGroup.LayoutParams.MATCH_PARENT));
|
||
choicePanel.setVisibility(View.GONE);'''
|
||
|
||
# ---- 5. 连接成功后改成进选择页 ----
|
||
O_CONN = ''' showControlPanel();
|
||
appendLog("已连接到 " + host);
|
||
toast("连接成功");'''
|
||
N_CONN = ''' // 用户要求:输入配对码连上之后先到选择页
|
||
// (远控 / 静默更新被控端),不直接进控制页。
|
||
//
|
||
// ⚠ 只有**这个**调用点改。下面"自动切到中转恢复"
|
||
// 那个 showControlPanel() 不改 —— 用户本来就在
|
||
// 远控页,中途弹选择页会打断他。
|
||
showChoicePanel();
|
||
appendLog("已连接到 " + host);
|
||
toast("连接成功");'''
|
||
|
||
PAIRS = [
|
||
("字段", F_ANCHOR, F_NEW),
|
||
("建面板方法", M_ANCHOR, M_NEW + M_ANCHOR),
|
||
("showOnly 数组", O_SO, N_SO),
|
||
("onCreate 建面板", O_OC, N_OC),
|
||
("onCreate 让位", O_OC2, N_OC2),
|
||
("onCreate 挂面板", O_OC3, N_OC3),
|
||
("连接成功后进选择页", O_CONN, N_CONN),
|
||
]
|
||
|
||
|
||
def main() -> int:
|
||
print("=" * 58)
|
||
print(" 手表端:连上后先到「远控 / 静默更新」选择页")
|
||
print("=" * 58)
|
||
print()
|
||
|
||
raw = MAIN.read_bytes()
|
||
bom = raw.startswith(b"\xef\xbb\xbf")
|
||
t = raw.decode("utf-8-sig")
|
||
|
||
if "buildChoicePanel" in t:
|
||
print(" 已经改过了")
|
||
return 0
|
||
|
||
missing = [(n, o.strip().splitlines()[0][:60]) for n, o, _w in PAIRS
|
||
if o not in t]
|
||
if missing:
|
||
print(" ✗ 这些锚点找不到:")
|
||
for n, s in missing:
|
||
print(" %s: %s" % (n, s))
|
||
return 1
|
||
print(" ✓ %d 个锚点全部就位" % len(PAIRS))
|
||
|
||
for _n, old, new in PAIRS:
|
||
t = t.replace(old, new, 1)
|
||
|
||
MAIN.write_bytes((b"\xef\xbb\xbf" if bom else b"") + t.encode("utf-8"))
|
||
print(" ✓ 加了 choicePanel + buildChoicePanel + showChoicePanel")
|
||
print(" ✓ 连接成功后改成进选择页(自动切中转那个没动)")
|
||
|
||
# 复核:确认"自动切中转"那个调用点没被误改
|
||
back = MAIN.read_text(encoding="utf-8")
|
||
n_choice = back.count("showChoicePanel();")
|
||
n_ctrl = back.count("showControlPanel();")
|
||
print()
|
||
print(" 复核:showChoicePanel 调用 %d 处(应 >=2:定义内 + 连接成功处)"
|
||
% n_choice)
|
||
print(" showControlPanel 调用 %d 处(自动切中转那个应该还在)"
|
||
% n_ctrl)
|
||
if "已切到中转服务器,连接恢复" in back:
|
||
i = back.find("已切到中转服务器,连接恢复")
|
||
seg = back[i:i + 200]
|
||
ok = "showControlPanel();" in seg
|
||
print(" [%s] 自动切中转恢复后仍是 showControlPanel" %
|
||
("PASS" if ok else "FAIL"))
|
||
if not ok:
|
||
return 1
|
||
return 0
|
||
|
||
|
||
if __name__ == "__main__":
|
||
sys.exit(main())
|