mirror of
https://gitee.com/xmz7891/ykxmz.git
synced 2026-10-18 10:10:43 +00:00
被控端 - 修复 MTU 问题:夹 MSS + 路径 MTU 探测(公网可用率 48% -> 100%) - 复用 HTTP 连接(免掉每次新建 TLS 的 30% 握手失败) - 每分钟回头找局域网,自动切回快 400 倍的路径 - 画面心跳帧:静止时省流量,客户端仍知道流是活的 - 锁屏时给出可操作的提示 手表端 - 内置 GTS Root R4 / GlobalSign Root CA,修 SSLHandshakeException - 连接错误翻译成人话(证书/时间/解析/超时分别提示) - 经中转连上后用服务器报的地址刷新已配对记录 - 新增局域网连接页面、服务器延迟检测 服务器端 - 支持 chunked 请求体(修每 16 秒一次的连接错乱与大面积 502) - 网络调优脚本 net-tune.sh(开机自动应用) - 管理页:批量删除、网络体检、下载区 - 隧道改 HTTP/2 + 双连接 文档 - 许可协议 / 安装前必读 / 安装后须知(作者:小麦子)
208 lines
8.3 KiB
Python
208 lines
8.3 KiB
Python
# -*- 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())
|