# -*- coding: utf-8 -*- r'''验「手表快捷栏」和「允许新设备连接」。 == 验什么 == 一、手表端源码 * 快捷栏建出来了,4 个格子(3 自定义 + 1 固定显示桌面) * 固定那个**不给改**(用户明确要求"其中有一个按钮不能改") * 挂进了 onCreate(光有方法不 addView 是白搭 —— 项目里踩过) * 加进了 showOnly 的数组(漏了的话它会永远压在最上面) * 7 个动作都在 * 设置页里有入口(没入口用户改不了) * 鼠标触摸板在 二、电脑端 allow_new_pairing 这个 feature * 能查、能关、能开 * 存储里真的变了 * 只影响新配对,不踢现有会话(注释里写了理由) == 为什么专门验"固定那个不给改" == 用户原话:"最多可以加 4 个,其中有一个按钮不能改,功能就是一键返回桌面"。 如果哪天有人"顺手"把它也做成可配置的,用户点了半天发现 "显示桌面"没了 —— 而这种回归不会报错,只会让人困惑。 所以这里断言:第 4 格永远是 desktop,且 QUICK_ACTIONS 里**没有**它。 ''' from __future__ import annotations import json import re import sys import tempfile from pathlib import Path ROOT = Path(__file__).resolve().parent.parent sys.path.insert(0, str(ROOT / "windows")) FAILS: list = [] def check(name: str, ok: bool, why: str = "") -> None: print(" [%s] %s%s" % ("PASS" if ok else "FAIL", name, (" —— " + why) if why else "")) if not ok: FAILS.append(name) def main() -> int: print("=" * 58) print(" 手表快捷栏 + 允许新设备连接") print("=" * 58) print() main_java = (ROOT / "android" / "src" / "com" / "lanremote" / "watch" / "MainActivity.java").read_text(encoding="utf-8") # ---------------- 一、快捷栏 ---------------- print("一、快捷栏(手表端)") for token, label in [ ("private void buildQuickBar()", "有 buildQuickBar"), ("private void refreshQuickBar()", "有 refreshQuickBar"), ("private void onQuickClick(int slot)", "有点击分发 onQuickClick"), ("private void runQuickAction(String id)", "有动作执行 runQuickAction"), ("private void loadQuickSlots()", "有读取设置"), ("private void saveQuickSlots()", "有保存设置"), ]: check(label, token in main_java) # 4 个格子 check("格子数 = 3 自定义 + 1 固定", "QUICK_CUSTOM_SLOTS + 1" in main_java and "QUICK_CUSTOM_SLOTS = 3" in main_java) # 固定那个 m = re.search(r'String id = \(slot < QUICK_CUSTOM_SLOTS\) \? quickSlots\[slot\]' r' : "desktop"', main_java) check("★ 第 4 格固定是 desktop(不给改)", m is not None, "第 4 格不是写死的 desktop —— 用户要求它不能改") check("★ QUICK_ACTIONS 里没有 desktop(否则它会出现在可选列表里)", not re.search(r'\{"desktop"', main_java), "desktop 出现在可选动作里了,用户就能把它改掉") check("desktop 走的是 show_desktop 命令", '"cmd", "show_desktop"' in main_java, "固定按钮应该让电脑 Win+D") # 7 个动作 print() print(" 7 个可选动作:") for aid, label in [("file", "传文件"), ("mirror", "屏幕镜像"), ("mouse", "鼠标控制"), ("sfx", "音效"), ("power", "关机"), ("tts", "语音播报"), ("image", "投图到电脑")]: ok = ('{"%s", "%s"}' % (aid, label)) in main_java check(" %s(%s)" % (aid, label), ok) # 挂接 print() print("二、挂接(漏了就白写)") oncreate = main_java[main_java.find("protected void onCreate"): main_java.find("protected void onCreate") + 6000] check("★ onCreate 里调了 buildQuickBar()", "buildQuickBar();" in oncreate, "没挂上去的话栏根本不显示") check("★ onCreate 里调了 loadQuickSlots()", "loadQuickSlots();" in oncreate) check("★ 快捷栏**最后** addView(保证在最上层)", oncreate.find("root.addView(quickBar") > oncreate.find("root.addView(mousePanel"), "加在面板之前会被面板整个盖住") check("★ showOnly 的数组里有 mousePanel", "settingsPanel, mousePanel};" in main_java, "漏了的话鼠标面板永远不隐藏") check("有 applyPanelTopMargin(面板给栏让位)", "private void applyPanelTopMargin" in main_java) # 设置页入口 print() print("三、设置页入口(没入口用户改不了)") check("有 fillSettingsExtra", "private void fillSettingsExtra()" in main_java) check("有 pickQuickSlot(选动作)", "private void pickQuickSlot(final int slot)" in main_java) check("设置页里加了「快捷栏」小节", 'sectionTitle("快捷栏")' in main_java) check("设置页里加了「允许新设备连接」小节", 'sectionTitle("允许新设备连接")' in main_java) check("界面上说明了第 4 格改不了", "固定的、改不了" in main_java, "不说明的话用户会一直找怎么改第四个") # 鼠标触摸板 print() print("四、鼠标触摸板") check("有 buildMousePanel", "private void buildMousePanel()" in main_java) check("用 GestureDetector(单击/双击/拖动)", "GestureDetector" in main_java and "onSingleTapConfirmed" in main_java and "onDoubleTap" in main_java and "onScroll" in main_java) check("单击 -> 左键", 'sendMouse("click", "left", 0)' in main_java) check("双击 -> 右键", 'sendMouse("click", "right", 0)' in main_java) check("滚轮上/下", 'sendMouse("scroll", "", 3)' in main_java and 'sendMouse("scroll", "", -3)' in main_java) check("拖动 -> moveby", '"moveby"' in main_java) check("isConnected() 存在(我第一版漏了,编译才发现)", "private boolean isConnected()" in main_java) # ---------------- 五、电脑端 allow_new_pairing ---------------- print() print("五、电脑端 allow_new_pairing feature") from lanremote.config import Store from lanremote.features import Features class FakeActions: """Features 要的是 actions 对象,不是 store(第一版就是这里传错了)。""" def __init__(self, store): self.store = store self.ui = None d = Path(tempfile.mkdtemp()) st = Store(d) feats = Features(FakeActions(st)) check("能查到当前状态", "允许" in feats.run("allow_new_pairing", {})) out = feats.run("allow_new_pairing", {"allow": False}) check("能关掉", "禁止" in out, "拿到 %r" % out) check(" ★ 存储里真的变了", st.get("allow_new_pairing") is False, "拿到 %r" % st.get("allow_new_pairing")) check(" 关掉之后再查也是关", "不允许" in feats.run("allow_new_pairing", {})) out = feats.run("allow_new_pairing", {"allow": True}) check("能打开", "已允许" in out, "拿到 %r" % out) check(" 存储里也变了", st.get("allow_new_pairing") is True) # 不存在名字要报错(别静默成功) try: feats.run("allow_new_pairing_typo", {}) check("不认识的名字会报错", False, "它竟然没报错") except ValueError: check("不认识的名字会报错", True) print() print("=" * 58) if FAILS: print(" 失败 %d 项:%s" % (len(FAILS), ", ".join(FAILS))) return 1 print(" 全部通过") return 0 if __name__ == "__main__": sys.exit(main())