Files
ykxmz/_tests/check_layout.py
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

208 lines
8.3 KiB
Python
Raw Permalink 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 -*-
"""手表屏幕适配校验。
分两部分:
1. **真机参数**(来自用户实测的诊断报告)—— 这是最该保证的
2. 一组常见 density 的覆盖,防止别的设备上按钮被挤爆
把 MainActivity.adaptGridToScreen() 的布局计算原样搬过来核算。
注意:这是**布局数学核算**,不是真机渲染测试。
没有可用设备/模拟器时,它能提前发现"screensize 一变按钮就挤爆"这类问题。
"""
from __future__ import annotations
import sys
PANEL_PADDING_DP = 8.0 # controlPanel 左右 padding
CELL_MARGIN_DP = 3.0 # 每个按钮的外边距
BUTTON_PADDING_DP = 4.0 # 按钮内部左右 padding
MIN_TOUCH_DP = 48.0 # Android 无障碍最小点击高度
# 现在手表端分 6 页,每页按钮数不一样。
# 关键指标变成「每一页都能一屏放下、不用滚动」。
# 现在分 10 页:功能页已删除,14 个分组散到各页。
# 括号里只统计**固定快捷按钮**;功能分组是滚动区,会显示在下面。
# 现在分 10 页:功能页已删除,14 个分组散到各页。
# 括号里只统计**固定快捷按钮**;功能分组是滚动区,会显示在下面。
# 每页最多 3 行(9 个),超了会滚动。
TABS = {
"媒体": ["播放/暂停", "停止", "上一曲", "下一曲", "音量 +", "音量 -", "静音"],
"屏幕": ["关掉画面", "屏幕镜像", "截屏", "投图到电脑", "放音到电脑"],
"文件": ["发送文件", "接收文件", "剪贴板→电脑", "取电脑剪贴板"],
"语音": ["语音播报", "音色", "朗读剪贴板", "电脑音乐", "弹通知"],
"电脑": ["快捷命令", "打开网址", "进程管理", "性能", "磁盘", "电量"],
"网络": ["本机监听端口", "确认能连上"],
"系统": ["回退上一步", "回退全部", "看能回退什么",
"锁屏", "睡眠", "关机", "重启", "取消定时关机", "定时关机"],
"教室": ["屏蔽游戏网站", "结束游戏进程", "一键恢复全部", "看有哪些限制"],
"效率": ["加待办", "看所有待办"],
"娱乐": ["掷骰子", "抽奖", "抛硬币"],
}
# 每页还挂着哪些功能分组(这些在滚动区里)
TAB_GROUPS = {
"媒体": ["音量", "媒体"],
"屏幕": ["显示"],
"文件": ["文件"],
"语音": ["静默"],
"电脑": ["查看", "工具"],
"网络": ["网络", "极客"],
"系统": ["系统"],
"教室": ["教室", "教学"],
"效率": ["效率"],
"娱乐": ["娱乐"],
}
LABELS = [label for group in TABS.values() for label in group]
# 用户实测的设备(来自遥控诊断报告)
REAL_DEVICE = {
"name": "M60T (hesc, Android 9 / API 28)",
"width_px": 492,
"height_px": 602,
"density": 1.4125,
"density_dpi": 226,
}
PASSED = 0
FAILED = 0
def check(name: str, ok: bool, detail: str = "") -> None:
global PASSED, FAILED
if ok:
PASSED += 1
print(f" [PASS] {name}")
else:
FAILED += 1
print(f" [FAIL] {name} {detail}")
def adapt(width_dp: float, height_dp: float):
"""与 Java 端 adaptGridToScreen() 一致。"""
if width_dp < 200:
return 1, 64.0, 16.0
if width_dp < 300:
columns, height, text = 2, 60.0, 14.0
elif width_dp < 330:
columns, height, text = 2, 64.0, 15.0
else:
columns, height, text = 3, 58.0, 13.0
if height_dp < 320 and columns == 2:
height = 56.0
return columns, height, text
def label_width_dp(label: str, text_sp: float) -> float:
"""估算文字宽度:中日韩字符约 1em,其余约 0.55em。"""
total = 0.0
for ch in label:
total += 1.0 if ord(ch) > 0x2E80 else 0.55
return total * text_sp
def report_one(tag: str, width_px: float, height_px: float, density: float) -> float:
"""核算一台设备,返回最紧的文字余量。"""
width_dp = width_px / density
height_dp = height_px / density
columns, cell_h, text_sp = adapt(width_dp, height_dp)
content_dp = width_dp - PANEL_PADDING_DP * 2
# 每行 columns 个按钮,每个按钮左右各一个 3dp 外边距
cell_w = (content_dp - CELL_MARGIN_DP * 2 * columns) / columns
text_area = cell_w - BUTTON_PADDING_DP * 2
print(f"\n{tag}")
print(f" {width_px:.0f}x{height_px:.0f}px density={density} -> "
f"{width_dp:.0f}x{height_dp:.0f} dp")
print(f" 列数={columns} 按钮高={cell_h:.0f}dp 字号={text_sp:.0f}sp "
f"按钮宽={cell_w:.1f}dp 可用文字宽={text_area:.1f}dp")
check(f"{tag} 按钮高度 >= {MIN_TOUCH_DP:.0f}dp", cell_h >= MIN_TOUCH_DP,
f"实际 {cell_h}dp")
longest = max(LABELS, key=lambda s: label_width_dp(s, text_sp))
need = label_width_dp(longest, text_sp)
lines = 1 if need <= text_area else 2
needed_h = lines * text_sp * 1.3 + 6
check(f"{tag} 最长文字「{longest}」放得下", needed_h <= cell_h,
f"需 {need:.0f}dp 宽 / {needed_h:.0f}dp 高,可用 {text_area:.0f}dp / {cell_h:.0f}dp")
print(f" 「{longest}」{need:.1f}dp -> {lines} 行,需高 {needed_h:.1f}dp")
margin = text_area / need
check(f"{tag} 文字余量 >= 95%", margin >= 0.95, f"余量 {margin * 100:.0f}%")
print(f" 文字余量 {margin * 100:.0f}%")
rows = (len(LABELS) + columns - 1) // columns
scroll_dp = rows * (cell_h + CELL_MARGIN_DP * 2)
print(f" 按钮 {len(LABELS)} 个 -> {rows} 行,总高约 {scroll_dp:.0f}dp"
f"(屏高 {height_dp:.0f}dp,需滚动 {max(0, scroll_dp - height_dp + 60):.0f}dp)")
return margin
def main() -> int:
print("=" * 74)
print("一、真机参数(来自用户实测的遥控诊断报告)")
print("=" * 74)
dev = REAL_DEVICE
report_one(f"实测设备: {dev['name']}", dev["width_px"], dev["height_px"],
dev["density"])
# ---- 分页:每页都要能一屏放下 ----
print()
print("=" * 74)
print("三、分页布局(每页不超过 7 个按钮)")
print("=" * 74)
width_dp = dev["width_px"] / dev["density"]
height_dp = dev["height_px"] / dev["density"]
columns, cell_h, text_sp = adapt(width_dp, height_dp)
# 顶栏 42dp + 标签栏 42dp + 日志约 60dp + 边距
chrome_dp = 42 + 42 + 62 + 20
available = height_dp - chrome_dp
for name, labels in TABS.items():
rows = (len(labels) + columns - 1) // columns
need = rows * (cell_h + 6)
fits = need <= available
check(f"「{name}」{len(labels)} 个按钮 / {rows} 行,一屏放得下",
fits, f"需要 {need:.0f}dp,可用 {available:.0f}dp")
print(f" {name}: {len(labels)} 个 / {rows} 行 / {need:.0f}dp"
f"(可用 {available:.0f}dp)")
real_cols = adapt(dev["width_px"] / dev["density"], dev["height_px"] / dev["density"])[0]
check(f"真机走 {real_cols} 列布局(348dp 宽应走三列以减少滚动)", real_cols == 3,
f"实际 {real_cols} 列")
# 报告里的 densityDpi 与 density 换算是否自洽
implied = dev["density"] * 160
check(f"density 与 densityDpi 自洽({dev['density']} x 160 = {implied:.0f},"
f"报告值 {dev['density_dpi']})",
abs(implied - dev["density_dpi"]) <= 2,
f"差 {abs(implied - dev['density_dpi']):.1f}")
print("\n" + "=" * 74)
print("二、同一分辨率下不同 density 的覆盖")
print("=" * 74)
worst = 1e9
two_col = 0
for density in [1.0, 1.25, 1.4125, 1.5, 1.75, 2.0, 2.5, 3.0]:
margin = report_one(f"density={density}",
dev["width_px"], dev["height_px"], density)
worst = min(worst, margin)
if adapt(dev["width_px"] / density, dev["height_px"] / density)[0] >= 2:
two_col += 1
print("\n" + "=" * 74)
print(f"最紧的一档文字余量: {worst * 100:.0f}%")
check("常见 density 下不会退化成单列", two_col >= 6,
f"多列档数 {two_col}")
print("\n" + "=" * 74)
print(f"通过 {PASSED} 项,失败 {FAILED} 项")
return 1 if FAILED else 0
if __name__ == "__main__":
sys.exit(main())