From 4d97e4028013c31618279e3b70d1fff773407857 Mon Sep 17 00:00:00 2001 From: xmz7891 Date: Fri, 9 Oct 2026 22:12:09 +0800 Subject: [PATCH] =?UTF-8?q?2.1.6=EF=BC=9A=E9=9D=99=E9=BB=98=E8=87=AA?= =?UTF-8?q?=E5=8A=A8=E6=9B=B4=E6=96=B0=20+=20=E8=BF=9C=E7=A8=8B=E9=BC=A0?= =?UTF-8?q?=E6=A0=87=E6=8E=A7=E5=88=B6=20+=20=E4=B8=80=E9=94=AE=E6=98=BE?= =?UTF-8?q?=E7=A4=BA=E6=A1=8C=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 用户这次提了一整套升级。这个提交先落地三块(其余几块在做)。 == 一、静默自动更新 == 用户原话:"手表端点击一个更新按钮,电脑端就可以自动进行静默更新, 其他什么设置都不变 …… 不弹任何窗口出来"。 链路: 手表点按钮 -> 被控端问中转"最新是哪版" -> 下载到 %TEMP%\ykxmz-update\ -> 校验 sha256 -> 起一个**分离的 .cmd 帮手** -> 自己退出 -> 帮手等它退出 -> 静默跑安装器 -> 重新拉起 关键设计说明: * **为什么要有那个 .cmd 帮手**:Windows 上正在运行的 exe 是锁着的, 安装器覆盖不了,所以必须"先退出、再装、再起来",而这三步 不能由被控端自己做(它一退出就没人执行后面的了)。 用 DETACHED_PROCESS 起的助手不受父进程退出影响。 * **配置为什么不会丢**:配置在 %ProgramData%\ykxmz\data\, **不在程序目录里**。安装器升级只替换 {app} 下的文件, 不碰 ProgramData —— 中转地址、配对码、各种开关原样保留。 这是这个功能能做的前提。 * **更新源是用户自己的中转站**,不是作者的服务器,也不是任何 写死的公网地址。被控端问的是它自己配置的那个地址。 中转没有这个接口(老版本服务端)时会明确提示"先手动升级一次服务端"。 * **sha256 必须校验**:自更新最大的风险是下到一半断了或被换了, 装上去是个坏程序、而且它还会自己再更新 —— 螺旋下去很难救。 对不上就**放弃这一轮**,保持现状(现状至少能用)。 中转新增:GET /v1/update/latest?platform=windows 返回 version / name / url / size / sha256。版本比较按**数字段** (不能按字符串,否则 "1.9.0" > "2.1.4")。sha256 按 (文件名,大小,mtime) 缓存,避免每次重算 9MB。 手表端:"电脑端版本:点这里查看" 按钮,查完显示 "电脑端 2.1.4 → 2.1.5(点这里更新)",确认后才真的更新。 == 二、远程鼠标控制 == 新增 windows/lanremote/input.py,用 SendInput 模拟鼠标键盘。 * **为什么用 SendInput 而不是 mouse_event**:后者是 Win95 时代的 API,会被 UIPI 拦,而且没有 dwExtraInfo(多个注入源会互相干扰)。 SendInput 还能一次打包多个事件,按下+抬起之间不会插进别的输入。 * **坐标是 0..65535 归一化**,而且**必须除以 (屏幕宽-1)**。 少了那个 -1,最右边和最下边几像素点不到 —— 表现为 "鼠标到不了屏幕边缘",任务栏在底下的机器上尤其明显。 * **INPUT 结构在 32/64 位下大小不同**(28 / 40), ctypes 里按进程位数算,不能写死。 * 权限说明:目标窗口如果是管理员权限的(UAC 提升过的), 普通权限的注入会被 UIPI 拦掉。这不是 bug,是 Windows 的设计。 被控端以管理员运行就都能点。 == 三、一键显示桌面 == 用户要求快捷栏里那个"不能改"的按钮就是这个功能,等于 Win+D。 和鼠标控制共用 input.py。 == 顺手修的一个自己造的坑 == 往 server.py 插方法时,**两次**把 dispatch 的兜底 `return {"ok": False, "error": f"未知命令: {cmd}"}` 一起卷进了新方法里: * pyflakes 报 "undefined name 'cmd'" * 而且 dispatch 丢了兜底分支(未知命令会返回 None) 第一次靠 pyflakes 抓到,第二次也是。为了以后不再犯, `_tests/test_update_flow.py` 里加了**结构断言**: 兜底必须在带 CMD_UPDATE 的那个方法体里,而各新方法里不能出现它。 (那条断言第一版写得太死 —— 要求"紧跟 CMD_UPDATE 分支", 后来往 dispatch 里插了新分支就误报了。改成验结构真相。) == 验证 == 全量回归(含新增的 3a10 / 3a11 两关)全部 PASS 静默自动更新:协议 + updater 15 项 + 服务端结构 8 项 远程鼠标:坐标归一化 / 滚轮夹取 / Win+D 事件顺序 / 非法参数 22 项 610 个 Python 文件语法通过 实际注入鼠标成功(move / click / scroll / show_desktop) == 还没做的(下一批) == 手表顶部快捷栏(可自定义最多 4 个,1 个固定"显示桌面") 屏幕镜像去掉提示条、去掉所有弹窗 托盘图标改成设置项 「允许新设备连接」开关(中转后台 + 安卓端) 弹文字窗口位置/大小可自定义 中转后台设备列表显示版本 + 远程点更新 == 顺手清理 == _tests/out_app/ 下面那堆 .class 是 Java 编译检查的产物, 以前误提交进仓库了(152 个文件)。这次从索引里移除并加进 .gitignore。 --- README.md | 16 +- _build/CHANGELOG.md | 40 +++ _build/add_site.py | 217 +++++++++++++ _build/add_update_endpoint.py | 206 ++++++++++++ _build/add_update_test_stage.py | 94 ++++++ _build/check_1panel_conflict.py | 93 ++++++ _build/check_server_detail.py | 80 +++++ _build/check_wp_ready.py | 83 +++++ _build/commit_msg_216.txt | 106 +++++++ _build/package_remote.py | 2 +- _build/wire_mouse_cmd.py | 162 ++++++++++ _build/wire_update_cmd.py | 242 ++++++++++++++ .../com/lanremote/watch/DeviceStore.class | Bin 5214 -> 0 bytes .../com/lanremote/watch/MainActivity$1.class | Bin 806 -> 0 bytes .../com/lanremote/watch/MainActivity$10.class | Bin 1123 -> 0 bytes .../lanremote/watch/MainActivity$100.class | Bin 5449 -> 0 bytes .../lanremote/watch/MainActivity$101.class | Bin 942 -> 0 bytes .../lanremote/watch/MainActivity$102.class | Bin 702 -> 0 bytes .../lanremote/watch/MainActivity$103.class | Bin 580 -> 0 bytes .../lanremote/watch/MainActivity$104.class | Bin 982 -> 0 bytes .../lanremote/watch/MainActivity$105.class | Bin 580 -> 0 bytes .../lanremote/watch/MainActivity$106.class | Bin 1620 -> 0 bytes .../lanremote/watch/MainActivity$107.class | Bin 741 -> 0 bytes .../lanremote/watch/MainActivity$108.class | Bin 2267 -> 0 bytes .../lanremote/watch/MainActivity$109.class | Bin 1946 -> 0 bytes .../com/lanremote/watch/MainActivity$11.class | Bin 904 -> 0 bytes .../lanremote/watch/MainActivity$110.class | Bin 1223 -> 0 bytes .../lanremote/watch/MainActivity$111.class | Bin 957 -> 0 bytes .../lanremote/watch/MainActivity$112$1.class | Bin 1115 -> 0 bytes .../lanremote/watch/MainActivity$112.class | Bin 1486 -> 0 bytes .../lanremote/watch/MainActivity$113.class | Bin 1601 -> 0 bytes .../lanremote/watch/MainActivity$114.class | Bin 1467 -> 0 bytes .../lanremote/watch/MainActivity$115.class | Bin 1518 -> 0 bytes .../lanremote/watch/MainActivity$116.class | Bin 945 -> 0 bytes .../lanremote/watch/MainActivity$117.class | Bin 985 -> 0 bytes .../lanremote/watch/MainActivity$118.class | Bin 911 -> 0 bytes .../lanremote/watch/MainActivity$119.class | Bin 1028 -> 0 bytes .../com/lanremote/watch/MainActivity$12.class | Bin 817 -> 0 bytes .../lanremote/watch/MainActivity$120.class | Bin 1504 -> 0 bytes .../lanremote/watch/MainActivity$121.class | Bin 922 -> 0 bytes .../lanremote/watch/MainActivity$122.class | Bin 723 -> 0 bytes .../lanremote/watch/MainActivity$123.class | Bin 2273 -> 0 bytes .../lanremote/watch/MainActivity$124.class | Bin 2140 -> 0 bytes .../lanremote/watch/MainActivity$125.class | Bin 1719 -> 0 bytes .../lanremote/watch/MainActivity$126.class | Bin 1361 -> 0 bytes .../lanremote/watch/MainActivity$127.class | Bin 554 -> 0 bytes .../lanremote/watch/MainActivity$128.class | Bin 555 -> 0 bytes .../lanremote/watch/MainActivity$129.class | Bin 828 -> 0 bytes .../com/lanremote/watch/MainActivity$13.class | Bin 1133 -> 0 bytes .../com/lanremote/watch/MainActivity$14.class | Bin 651 -> 0 bytes .../com/lanremote/watch/MainActivity$15.class | Bin 929 -> 0 bytes .../com/lanremote/watch/MainActivity$16.class | Bin 983 -> 0 bytes .../com/lanremote/watch/MainActivity$17.class | Bin 620 -> 0 bytes .../com/lanremote/watch/MainActivity$18.class | Bin 615 -> 0 bytes .../com/lanremote/watch/MainActivity$19.class | Bin 615 -> 0 bytes .../com/lanremote/watch/MainActivity$2.class | Bin 1400 -> 0 bytes .../com/lanremote/watch/MainActivity$20.class | Bin 615 -> 0 bytes .../com/lanremote/watch/MainActivity$21.class | Bin 616 -> 0 bytes .../com/lanremote/watch/MainActivity$22.class | Bin 618 -> 0 bytes .../com/lanremote/watch/MainActivity$23.class | Bin 615 -> 0 bytes .../com/lanremote/watch/MainActivity$24.class | Bin 750 -> 0 bytes .../com/lanremote/watch/MainActivity$25.class | Bin 545 -> 0 bytes .../com/lanremote/watch/MainActivity$26.class | Bin 545 -> 0 bytes .../com/lanremote/watch/MainActivity$27.class | Bin 622 -> 0 bytes .../com/lanremote/watch/MainActivity$28.class | Bin 622 -> 0 bytes .../com/lanremote/watch/MainActivity$29.class | Bin 618 -> 0 bytes .../com/lanremote/watch/MainActivity$3.class | Bin 1454 -> 0 bytes .../com/lanremote/watch/MainActivity$30.class | Bin 545 -> 0 bytes .../com/lanremote/watch/MainActivity$31.class | Bin 545 -> 0 bytes .../com/lanremote/watch/MainActivity$32.class | Bin 545 -> 0 bytes .../com/lanremote/watch/MainActivity$33.class | Bin 545 -> 0 bytes .../com/lanremote/watch/MainActivity$34.class | Bin 545 -> 0 bytes .../com/lanremote/watch/MainActivity$35.class | Bin 770 -> 0 bytes .../com/lanremote/watch/MainActivity$36.class | Bin 545 -> 0 bytes .../com/lanremote/watch/MainActivity$37.class | Bin 545 -> 0 bytes .../com/lanremote/watch/MainActivity$38.class | Bin 545 -> 0 bytes .../com/lanremote/watch/MainActivity$39.class | Bin 545 -> 0 bytes .../com/lanremote/watch/MainActivity$4.class | Bin 719 -> 0 bytes .../com/lanremote/watch/MainActivity$40.class | Bin 545 -> 0 bytes .../com/lanremote/watch/MainActivity$41.class | Bin 545 -> 0 bytes .../com/lanremote/watch/MainActivity$42.class | Bin 545 -> 0 bytes .../com/lanremote/watch/MainActivity$43.class | Bin 545 -> 0 bytes .../com/lanremote/watch/MainActivity$44.class | Bin 868 -> 0 bytes .../com/lanremote/watch/MainActivity$45.class | Bin 721 -> 0 bytes .../com/lanremote/watch/MainActivity$46.class | Bin 749 -> 0 bytes .../com/lanremote/watch/MainActivity$47.class | Bin 745 -> 0 bytes .../com/lanremote/watch/MainActivity$48.class | Bin 752 -> 0 bytes .../com/lanremote/watch/MainActivity$49.class | Bin 647 -> 0 bytes .../com/lanremote/watch/MainActivity$5.class | Bin 649 -> 0 bytes .../com/lanremote/watch/MainActivity$50.class | Bin 648 -> 0 bytes .../com/lanremote/watch/MainActivity$51.class | Bin 651 -> 0 bytes .../com/lanremote/watch/MainActivity$52.class | Bin 650 -> 0 bytes .../com/lanremote/watch/MainActivity$53.class | Bin 545 -> 0 bytes .../com/lanremote/watch/MainActivity$54.class | Bin 545 -> 0 bytes .../com/lanremote/watch/MainActivity$55.class | Bin 881 -> 0 bytes .../com/lanremote/watch/MainActivity$56.class | Bin 868 -> 0 bytes .../com/lanremote/watch/MainActivity$57.class | Bin 875 -> 0 bytes .../com/lanremote/watch/MainActivity$58.class | Bin 873 -> 0 bytes .../com/lanremote/watch/MainActivity$59.class | Bin 936 -> 0 bytes .../com/lanremote/watch/MainActivity$6.class | Bin 649 -> 0 bytes .../com/lanremote/watch/MainActivity$60.class | Bin 867 -> 0 bytes .../com/lanremote/watch/MainActivity$61.class | Bin 859 -> 0 bytes .../com/lanremote/watch/MainActivity$62.class | Bin 859 -> 0 bytes .../com/lanremote/watch/MainActivity$63.class | Bin 864 -> 0 bytes .../com/lanremote/watch/MainActivity$64.class | Bin 706 -> 0 bytes .../com/lanremote/watch/MainActivity$65.class | Bin 1539 -> 0 bytes .../com/lanremote/watch/MainActivity$66.class | Bin 698 -> 0 bytes .../com/lanremote/watch/MainActivity$67.class | Bin 683 -> 0 bytes .../com/lanremote/watch/MainActivity$68.class | Bin 1473 -> 0 bytes .../com/lanremote/watch/MainActivity$69.class | Bin 2106 -> 0 bytes .../com/lanremote/watch/MainActivity$7.class | Bin 649 -> 0 bytes .../com/lanremote/watch/MainActivity$70.class | Bin 654 -> 0 bytes .../com/lanremote/watch/MainActivity$71.class | Bin 996 -> 0 bytes .../com/lanremote/watch/MainActivity$72.class | Bin 916 -> 0 bytes .../com/lanremote/watch/MainActivity$73.class | Bin 1002 -> 0 bytes .../com/lanremote/watch/MainActivity$74.class | Bin 952 -> 0 bytes .../com/lanremote/watch/MainActivity$75.class | Bin 1103 -> 0 bytes .../com/lanremote/watch/MainActivity$76.class | Bin 1113 -> 0 bytes .../com/lanremote/watch/MainActivity$77.class | Bin 1504 -> 0 bytes .../com/lanremote/watch/MainActivity$78.class | Bin 1102 -> 0 bytes .../lanremote/watch/MainActivity$79$1.class | Bin 1623 -> 0 bytes .../com/lanremote/watch/MainActivity$79.class | Bin 1846 -> 0 bytes .../com/lanremote/watch/MainActivity$8.class | Bin 649 -> 0 bytes .../lanremote/watch/MainActivity$80$1$1.class | Bin 1176 -> 0 bytes .../lanremote/watch/MainActivity$80$1.class | Bin 2076 -> 0 bytes .../com/lanremote/watch/MainActivity$80.class | Bin 1515 -> 0 bytes .../com/lanremote/watch/MainActivity$81.class | Bin 2403 -> 0 bytes .../com/lanremote/watch/MainActivity$82.class | Bin 1376 -> 0 bytes .../com/lanremote/watch/MainActivity$83.class | Bin 798 -> 0 bytes .../com/lanremote/watch/MainActivity$84.class | Bin 1238 -> 0 bytes .../com/lanremote/watch/MainActivity$85.class | Bin 1139 -> 0 bytes .../com/lanremote/watch/MainActivity$86.class | Bin 1027 -> 0 bytes .../com/lanremote/watch/MainActivity$87.class | Bin 2075 -> 0 bytes .../com/lanremote/watch/MainActivity$88.class | Bin 1395 -> 0 bytes .../com/lanremote/watch/MainActivity$89.class | Bin 559 -> 0 bytes .../com/lanremote/watch/MainActivity$9.class | Bin 556 -> 0 bytes .../com/lanremote/watch/MainActivity$90.class | Bin 652 -> 0 bytes .../com/lanremote/watch/MainActivity$91.class | Bin 832 -> 0 bytes .../com/lanremote/watch/MainActivity$92.class | Bin 1217 -> 0 bytes .../com/lanremote/watch/MainActivity$93.class | Bin 1468 -> 0 bytes .../com/lanremote/watch/MainActivity$94.class | Bin 554 -> 0 bytes .../com/lanremote/watch/MainActivity$95.class | Bin 647 -> 0 bytes .../com/lanremote/watch/MainActivity$96.class | Bin 647 -> 0 bytes .../com/lanremote/watch/MainActivity$97.class | Bin 1310 -> 0 bytes .../com/lanremote/watch/MainActivity$98.class | Bin 2276 -> 0 bytes .../com/lanremote/watch/MainActivity$99.class | Bin 1033 -> 0 bytes .../lanremote/watch/MainActivity$Act.class | Bin 458 -> 0 bytes .../lanremote/watch/MainActivity$TabDef.class | Bin 634 -> 0 bytes .../com/lanremote/watch/MainActivity.class | Bin 82397 -> 0 bytes _tests/test_apk_contents.py | 2 +- _tests/test_mouse_control.py | 192 ++++++++++++ _tests/test_package_remote.py | 8 +- _tests/test_update_flow.py | 209 +++++++++++++ _tests/verify_all.ps1 | 23 ++ android/AndroidManifest.xml | 2 +- android/build_apk.ps1 | 4 +- .../src/com/lanremote/watch/MainActivity.java | 171 ++++++++++ docs/AFTER-INSTALL.txt | 6 +- docs/BEFORE-INSTALL.txt | 4 +- docs/LICENSE.txt | 6 +- installer/license-combined.txt | 16 +- installer/ykxmz.iss | 4 +- relay/relay.py | 104 ++++++- windows/lanremote/__init__.py | 2 +- windows/lanremote/input.py | 284 +++++++++++++++++ windows/lanremote/protocol.py | 7 + windows/lanremote/server.py | 176 +++++++++++ windows/lanremote/updater.py | 294 ++++++++++++++++++ windows/version_info.txt | 4 +- 169 files changed, 2820 insertions(+), 39 deletions(-) create mode 100644 _build/add_site.py create mode 100644 _build/add_update_endpoint.py create mode 100644 _build/add_update_test_stage.py create mode 100644 _build/check_1panel_conflict.py create mode 100644 _build/check_server_detail.py create mode 100644 _build/check_wp_ready.py create mode 100644 _build/commit_msg_216.txt create mode 100644 _build/wire_mouse_cmd.py create mode 100644 _build/wire_update_cmd.py delete mode 100644 _tests/out_app/com/lanremote/watch/DeviceStore.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$1.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$10.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$100.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$101.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$102.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$103.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$104.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$105.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$106.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$107.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$108.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$109.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$11.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$110.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$111.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$112$1.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$112.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$113.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$114.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$115.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$116.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$117.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$118.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$119.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$12.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$120.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$121.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$122.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$123.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$124.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$125.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$126.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$127.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$128.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$129.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$13.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$14.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$15.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$16.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$17.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$18.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$19.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$2.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$20.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$21.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$22.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$23.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$24.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$25.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$26.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$27.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$28.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$29.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$3.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$30.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$31.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$32.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$33.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$34.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$35.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$36.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$37.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$38.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$39.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$4.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$40.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$41.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$42.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$43.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$44.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$45.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$46.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$47.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$48.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$49.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$5.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$50.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$51.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$52.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$53.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$54.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$55.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$56.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$57.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$58.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$59.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$6.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$60.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$61.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$62.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$63.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$64.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$65.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$66.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$67.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$68.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$69.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$7.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$70.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$71.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$72.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$73.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$74.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$75.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$76.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$77.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$78.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$79$1.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$79.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$8.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$80$1$1.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$80$1.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$80.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$81.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$82.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$83.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$84.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$85.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$86.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$87.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$88.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$89.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$9.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$90.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$91.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$92.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$93.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$94.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$95.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$96.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$97.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$98.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$99.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$Act.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity$TabDef.class delete mode 100644 _tests/out_app/com/lanremote/watch/MainActivity.class create mode 100644 _tests/test_mouse_control.py create mode 100644 _tests/test_update_flow.py create mode 100644 windows/lanremote/input.py create mode 100644 windows/lanremote/updater.py diff --git a/README.md b/README.md index 14bbf11..26fe05b 100644 --- a/README.md +++ b/README.md @@ -45,9 +45,9 @@ | 装在哪 | 是什么 | 文件 | |---|---|---| -| 教室电脑(Windows) | 被控端 | `ykxmz-setup-2.1.5.exe` | -| 小服务器(随身 WiFi / 云主机 / 树莓派) | 中转站 | `ykxmz-server-2.1.5.zip` | -| 手表(安卓) | 控制端 | `ykxmz-watch-2.1.5.apk` | +| 教室电脑(Windows) | 被控端 | `ykxmz-setup-2.1.6.exe` | +| 小服务器(随身 WiFi / 云主机 / 树莓派) | 中转站 | `ykxmz-server-2.1.6.zip` | +| 手表(安卓) | 控制端 | `ykxmz-watch-2.1.6.apk` | **只在一个教室用?** 那第三件(中转站)可以先不装,手表和电脑连同一个 WiFi 就能直连。 @@ -81,7 +81,7 @@ ### 第一步:电脑上装被控端 -双击 `ykxmz-setup-2.1.5.exe`,按提示装完(默认装到 `C:\Program Files\ykxmz`)。 +双击 `ykxmz-setup-2.1.6.exe`,按提示装完(默认装到 `C:\Program Files\ykxmz`)。 第一次运行会弹出窗口,填两样: @@ -94,12 +94,12 @@ > 想再打开窗口,**再双击一次程序图标**就行,不会起第二个进程。 > 想彻底退出,窗口上点「退出」。 -> 不想用安装包的话,`ykxmz-agent-2.1.5.zip` 是绿色版: +> 不想用安装包的话,`ykxmz-agent-2.1.6.zip` 是绿色版: > 解压出来直接跑 `ykxmz.exe`,整个文件夹拷走就能换台机器用,不写注册表。 ### 第二步:手表上装 App -把 `ykxmz-watch-2.1.5.apk` 传到手表装上。手表可能提示"未知来源",去设置里允许一下。 +把 `ykxmz-watch-2.1.6.apk` 传到手表装上。手表可能提示"未知来源",去设置里允许一下。 打开 App,首页有四个入口: @@ -223,7 +223,7 @@ 中转站就是**一台能上外网的 Linux 小机器**(随身 WiFi、树莓派、云主机都行, Debian 12 / Ubuntu 实测可用,512 MB 内存就够)。 -把 `ykxmz-server-2.1.5.zip` 解压到那台机器上,然后一条命令: +把 `ykxmz-server-2.1.6.zip` 解压到那台机器上,然后一条命令: ```bash bash deploy.sh @@ -456,7 +456,7 @@ windows\build_exe.ps1 ```powershell android\build_apk.ps1 # 流程:aapt2 → javac → d8 → zipalign → apksigner -# 产物:dist\ykxmz-watch-2.1.5.apk +# 产物:dist\ykxmz-watch-2.1.6.apk ``` 最低支持 Android 7.0(API 24),targetSdk 34。 diff --git a/_build/CHANGELOG.md b/_build/CHANGELOG.md index 610cf23..90d5cfa 100644 --- a/_build/CHANGELOG.md +++ b/_build/CHANGELOG.md @@ -1,3 +1,43 @@ +## 2.1.6 + +- **电脑端静默自动更新** —— 手表上点一下,电脑自己升到最新版, + 全程不弹任何窗口,**中转地址、配对码、所有设置一个都不变**。 + + 流程:手表点按钮 → 电脑端问中转"最新是哪版" → 下载并校验 → + 起一个后台帮手 → 自己退出 → 帮手等它退出后静默装好 → 重新拉起。 + + 几个关键点: + + - **配置为什么不会丢**:配置存在 `%ProgramData%\ykxmz\data\`, + 不在程序目录里。安装器升级只替换程序文件,不碰这个目录。 + - **更新源是用户自己的中转站**,不是作者的服务器,也不是任何 + 写死的地址。中转没这功能(老版本服务端)时会明确提示 + "先手动升级一次服务端"。 + - **校验和不匹配就放弃这一轮**,保持现状(现状至少能用)。 + 自更新最怕的就是装上一个坏程序、然后它还自己继续更新。 + + 手表首页新增「电脑端版本」按钮,点开就能看到 + "电脑端 2.1.4 → 2.1.5",确认后才动手。 + +- **远程鼠标控制**:移动、左键、右键、中键、滚轮(含横向)。 + + 这是"能直接操控电脑"的功能,所以参数校验写得很严: + 非法动作、非法按键、非数字坐标全都会被拒。 + +- **一键显示桌面**(等于 Win+D)。 + +- **Windows 7 兼容性校验关卡**:打包时和回归里都会检查 + (内嵌的是不是 3.8 运行库、**有没有**那个 Win7 上不存在的 DLL、 + exe 是不是 32 位),不过就拒绝出包。 + +- 技术说明: + - 鼠标坐标用 SendInput 的 **0..65535 归一化**,而且**必须除以 + (屏幕宽-1)**。少了那个 -1,最右边和最下边几像素点不到。 + - `INPUT` 结构在 32/64 位下大小不同(28 / 40),代码按进程位数算。 + - 目标窗口若是管理员权限的(UAC 提升过的),普通权限的注入会被 + 系统拦掉 —— 这是 Windows 的设计,不是 bug。被控端以管理员运行 + 就没问题。 + ## 2.1.5 - **修「Windows 7 装完报 dll 缺失」** —— 学校电脑装不上。 diff --git a/_build/add_site.py b/_build/add_site.py new file mode 100644 index 0000000..e9d394f --- /dev/null +++ b/_build/add_site.py @@ -0,0 +1,217 @@ +# -*- coding: utf-8 -*- +r'''在一台服务器上加一个新网站(nginx 虚拟主机 + 证书)。 + +== 为什么需要这个 == + +一台服务器上跑多个网站是常态。做法是:**一个 nginx 占 80/443, +每个域名一个 server 块**,nginx 按 Host 头分流到各自的本地端口。 + +手写这套配置每次都要复制粘贴改半天,还容易漏: + * 忘了签证书 -> 浏览器报不安全 + * 后端端口写错 -> 502 + * 忘了把后端绑到 127.0.0.1 -> 服务直接暴露在公网 + +所以做成一键的。 + +== 用法 == + + python _build/add_site.py 域名 后端地址 [--php] [--root 目录] + + 例: + # 反代到一个本地服务 + python _build/add_site.py blog.xxmxxs.top 127.0.0.1:9000 + + # WordPress 那种 PHP 站(走 php-fpm 的 unix socket 或端口) + python _build/add_site.py xxmxxs.top 127.0.0.1:9000 --php --root /opt/1panel/www/sites/xxmxxs.top/index + +== 设计上的几个注意 == + +1. **默认只回源 127.0.0.1** + 后端服务必须只绑本地。写 0.0.0.0 的话,别人直接打 IP:端口 + 就绕过 nginx 和 Cloudflare 了 —— 那正是用户不想要的。 + +2. **证书用 certbot 的 nginx 插件签** + 它自己改配置、自己加 443。前提是**这个域名的 A 记录已经指过来** + (而且最好是橙色云朵,这样 certbot 的 HTTP-01 挑战能经 Cloudflare 回源)。 + +3. **不动别的站点** + 只新建 /etc/nginx/sites-available/<域名>,再链到 sites-enabled。 + 已有的站点(比如遥控中转 ykxmz)一个字都不碰。 + +4. **默认 server 的坑** + 如果有人用 IP 直接访问,nginx 会用**第一个** server 块回应。 + 所以配置文件开头有个显式的 default server 把它们挡掉 —— + 这样 IP:port 扫不到任何真实站点。 +''' +from __future__ import annotations + +import argparse +import sys +from pathlib import Path + +ROOT = Path(__file__).resolve().parent.parent +sys.path.insert(0, str(ROOT / "_build")) +from lssh import run, upload # noqa: E402 + +TEMPLATE = """# {domain} —— 由 _build/add_site.py 生成 +# +# 后端: {backend} +# 说明: nginx 按 server_name 分流;后端只应该监听 127.0.0.1, +# 这样外面只能通过域名(也就是经过 nginx + Cloudflare)访问。 + +server {{ + listen 80; + listen [::]:80; + server_name {domain}; + + access_log /var/log/nginx/{safe}_access.log; + error_log /var/log/nginx/{safe}_error.log; + +{body} + server_tokens off; + client_max_body_size {max_body}; +}} +""" + +PROXY_BODY = """ location / {{ + proxy_pass http://{backend}; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_read_timeout 300s; + proxy_connect_timeout 30s; + }} +""" + +PHP_BODY = """ root {root}; + index index.php index.html; + + location / {{ + try_files $uri $uri/ /index.php?$args; + }} + + location ~ \\.php$ {{ + include snippets/fastcgi-php.conf; + fastcgi_pass {backend}; + fastcgi_read_timeout 300s; + }} + + location ~ /\\.ht {{ + deny all; + }} +""" + + +def main() -> int: + ap = argparse.ArgumentParser(description="加一个网站") + ap.add_argument("domain", help="域名,例如 blog.xxmxxs.top") + ap.add_argument("backend", help="后端地址,例如 127.0.0.1:9000") + ap.add_argument("--php", action="store_true", + help="PHP 站(用 fastcgi_pass 而不是 proxy_pass)") + ap.add_argument("--root", default="", help="PHP 站的根目录") + ap.add_argument("--max-body", default="64m", help="上传大小上限,默认 64m") + ap.add_argument("--no-cert", action="store_true", + help="只配 80,先不签证书") + args = ap.parse_args() + + domain = args.domain.strip() + backend = args.backend.strip() + safe = domain.replace(".", "_").replace("-", "_") + + # ⚠ 后端必须是本地地址。写 0.0.0.0 或公网地址的话, + # 等于把后端服务直接暴露出去,nginx 和 Cloudflare 都白搭。 + if not (backend.startswith("127.0.0.1:") or backend.startswith("localhost:") + or backend.startswith("unix:")): + print(" ⚠ 后端 %r 不是本地地址。" % backend) + print(" 后端必须只监听 127.0.0.1 —— 否则别人直接打 " + "IP:端口就绕过了 nginx 和 Cloudflare。") + print(" 要继续的话请显式确认(现在中止)。") + return 1 + + if args.php and not args.root: + print(" ✗ --php 需要同时给 --root(网站文件目录)") + return 1 + + body = (PHP_BODY.format(root=args.root, backend=backend) + if args.php else PROXY_BODY.format(backend=backend)) + conf = TEMPLATE.format(domain=domain, backend=backend, safe=safe, + body=body, max_body=args.max_body) + + print("=" * 58) + print(" 加站点 %s -> %s" % (domain, backend)) + print("=" * 58) + + print() + print(" ── 1. 写配置 ──") + import tempfile + tmp = Path(tempfile.gettempdir()) / ("nginx-%s.conf" % safe) + tmp.write_text(conf, encoding="utf-8", newline="\n") + upload(str(tmp), "/tmp/%s.conf" % safe) + out = run( + "sudo mv /tmp/{s}.conf /etc/nginx/sites-available/{d} && " + "sudo ln -sf /etc/nginx/sites-available/{d} " + "/etc/nginx/sites-enabled/{d} && " + "sudo nginx -t 2>&1 | tail -2".format(s=safe, d=domain), + timeout=180, quiet=True) + for ln in out[1].strip().splitlines(): + print(" " + ln) + if "successful" not in out[1]: + print(" ✗ nginx 配置检查没过,撤回") + run("sudo rm -f /etc/nginx/sites-enabled/%s" % domain, + timeout=60, quiet=True) + return 1 + + print() + print(" ── 2. 重载 nginx ──") + out = run("sudo systemctl reload nginx && sleep 1 && " + "systemctl is-active nginx", timeout=120, quiet=True) + print(" nginx: " + out[1].strip()) + + if not args.no_cert: + print() + print(" ── 3. 签 Let's Encrypt 证书(顺带加上 443 和跳转)──") + out = run( + "sudo certbot --nginx -d {d} --non-interactive --agree-tos " + "--register-unsafely-without-email --redirect " + "--keep-until-expiring 2>&1 | tail -8".format(d=domain), + timeout=600, quiet=True) + for ln in out[1].strip().splitlines(): + print(" " + ln) + + print() + print(" ── 4. 复核 ──") + # ⚠ 这里**不能用 .format()** —— 远端命令里有 curl 的 `%{http_code}`, + # 而 .format() 会把 `{http_code}` 当占位符,抛 + # "KeyError/IndexError: http_code"。 + # 和之前用 % 格式化踩的是**同一类坑**(内容里有格式符)。 + # 凡是把域名等变量拼进含 `{...}` 的远端命令,一律用 replace。 + probe = ( + 'printf " %-34s %s\\n" "本机带 Host 头" ' + '"$(curl -s -o /dev/null -w \'%{http_code}\' --max-time 10 ' + '-H \'Host: __D__\' http://127.0.0.1:80/)"; ' + 'printf " %-34s %s\\n" "本机 443 带 SNI" ' + '"$(curl -sk -o /dev/null -w \'%{http_code}\' --max-time 10 ' + '--resolve __D__:443:127.0.0.1 https://__D__/)"; ' + 'printf " %-34s %s\\n" "公网 443" ' + '"$(curl -s -o /dev/null -w \'%{http_code}\' --max-time 20 ' + 'https://__D__/)"' + ).replace("__D__", domain) + out = run(probe, timeout=180, quiet=True) + for ln in out[1].strip().splitlines(): + print(ln) + + print() + print(" 已加好。注意:") + print(" * 这个域名的 DNS 要指向这台服务器(Cloudflare 橙色云朵)") + print(" * 后端 %s 必须只监听 127.0.0.1" % backend) + print(" * 不要动 /etc/nginx/sites-available/ykxmz(那是遥控中转)", + ) + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/_build/add_update_endpoint.py b/_build/add_update_endpoint.py new file mode 100644 index 0000000..0abb3c8 --- /dev/null +++ b/_build/add_update_endpoint.py @@ -0,0 +1,206 @@ +# -*- coding: utf-8 -*- +r'''给中转加「最新版本查询」接口 —— 被控端静默自动更新的第一步。 + +== 为什么由中转来回答 == + +被控端已经知道中转地址(那是它的配置),而中转的 downloads/ 目录里 +就放着各版本的成品。让中转直接回答"最新是哪版、在哪下、校验和多少", +就不用把任何地址写死在客户端里 —— 换个域名/换台服务器都不用改代码。 + +== 新增 == + + GET /v1/update/latest?platform=windows + + 200 { + "ok": true, + "platform": "windows", + "version": "2.1.5", + "name": "ykxmz-setup-2.1.5.exe", + "url": "/download/ykxmz-setup-2.1.5.exe", + "size": 9395078, + "sha256": "....", + "notes": "..." <- 可选,取 CHANGELOG 里那一节 + } + + 没找到任何包时: + 200 {"ok": false, "error": "还没有可用的安装包"} + +== url 给相对路径 == + +给相对路径(`/download/xxx.exe`),让被控端用**它自己配置的中转地址** +拼成完整 URL。这样: + * 中转换域名/换 IP 不用改任何东西 + * 走 Cloudflare 还是直连由被控端那边的配置决定 + +== sha256 要缓存 == + +安装包 9MB,每次请求都算一遍 sha256 大概 30 毫秒 —— 不算慢, +但这个接口会被所有被控端周期性调用(每台每小时一次的话, +一个学校几十台就是几十次),没必要重复算。 +按「文件名 + 大小 + 修改时间」做键缓存。 +''' +from __future__ import annotations + +import sys +from pathlib import Path + +ROOT = Path(__file__).resolve().parent.parent +RELAY = ROOT / "relay" / "relay.py" + + +def main() -> int: + raw = RELAY.read_bytes() + bom = raw.startswith(b"\xef\xbb\xbf") + text = raw.decode("utf-8-sig") + + if "_latest_package" in text: + print(" 已经加过了") + return 0 + + # ⚠ **先把所有锚点都验一遍,再动手改。** + # + # 第一版是把替换都做在内存里、最后才 write_bytes —— + # 结果中途某个锚点没匹配上就 `return 1`, + # 前面几处改动**全丢了**(脚本还打印了"✓ 已加"), + # 而文件其实一个字没变。特别容易误判成"加好了"。 + # + # 教训:**批处理脚本要幺先验后改,幺写一段落一段。** + ANCHORS = { + "import_json": "\nimport json", + "list_downloads": ' items.sort(key=lambda item: item["mtime"], reverse=True)\n return items\n', + "downloads_route": ' if path == "/v1/downloads":', + } + missing = [k for k, v in ANCHORS.items() if v not in text] + if missing: + print(" ✗ 这些锚点找不到,一个字都不改:%s" % ", ".join(missing)) + return 1 + print(" ✓ 锚点全部就位") + + # ---- 1. import hashlib ---- + if "\nimport hashlib" not in text: + text = text.replace(ANCHORS["import_json"], + "\nimport hashlib\nimport json", 1) + print(" ✓ 加了 import hashlib") + + # ---- 2. 插到 _list_downloads 之后 ---- + anchor = ANCHORS["list_downloads"] + + helper = anchor + ''' + +# 最新安装包的信息缓存。键 = (文件名, 大小, 修改时间)。 +_PKG_CACHE: dict = {} + + +def _sha256_of(path: Path) -> str: + """算文件的 sha256(分块读,别把 9MB 全塞内存)。""" + h = hashlib.sha256() + with path.open("rb") as f: + for chunk in iter(lambda: f.read(256 * 1024), b""): + h.update(chunk) + return h.hexdigest() + + +def _latest_package(platform: str = "windows") -> dict: + """找 downloads/ 里最新的安装包,给被控端自更新用。 + + `platform` 现在只有 windows;留着参数是为了以后加别的平台 + (手表/服务端不通过这个接口自更新,它们各有各的更新方式)。 + + 返回 {} 表示"还没有可用的包"。 + """ + # 各平台的成品名前缀 + prefix = { + "windows": "ykxmz-setup-", + }.get(platform) + if not prefix: + return {} + + best = None # (版本元组, 路径) + try: + if not DOWNLOADS_DIR.is_dir(): + return {} + for entry in DOWNLOADS_DIR.iterdir(): + if not entry.is_file(): + continue + name = entry.name + if not name.startswith(prefix) or not name.endswith(".exe"): + continue + if not _safe_download_name(name): + continue + # 从文件名里抠版本号 + mid = name[len(prefix):-len(".exe")] + parts = mid.split(".") + if len(parts) != 3 or not all(p.isdigit() for p in parts): + continue + ver = tuple(int(p) for p in parts) + # ⚠ 按**数字段**比大小,不能按字符串 —— + # 否则 "1.9.0" > "2.1.4"(字符 '9' > '1')。 + if best is None or ver > best[0]: + best = (ver, entry) + except OSError: + return {} + + if best is None: + return {} + + ver, path = best + try: + st = path.stat() + except OSError: + return {} + + key = (path.name, st.st_size, int(st.st_mtime)) + cached = _PKG_CACHE.get(key) + if cached: + digest = cached + else: + try: + digest = _sha256_of(path) + except OSError: + return {} + # 只留最近几个,别让字典涨 + _PKG_CACHE.clear() + _PKG_CACHE[key] = digest + + return { + "platform": platform, + "version": ".".join(str(p) for p in ver), + "name": path.name, + "url": "/download/" + quote(path.name), + "size": st.st_size, + "sha256": digest, + "mtime": st.st_mtime, + } +''' + text = text.replace(anchor, helper, 1) + print(" ✓ 加了 _latest_package / _sha256_of") + + # ---- 3. 路由 ---- + route_anchor = ANCHORS["downloads_route"] + + route = ''' if path == "/v1/update/latest": + # 被控端自更新用:最新版是哪版、在哪下、校验和多少。 + # 公开接口 —— 文件名和哈希本来就在下载页上人人可见, + # 不含任何敏感信息。 + platform = (urllib.parse.parse_qs( + urllib.parse.urlparse(self.path).query) + .get("platform", ["windows"])[0] or "windows") + pkg = _latest_package(platform) + if not pkg: + self._json(200, {"ok": False, + "error": "还没有可用的安装包"}) + else: + self._json(200, dict(pkg, ok=True)) + return + +''' + route_anchor + text = text.replace(route_anchor, route, 1) + print(" ✓ 加了 /v1/update/latest 路由") + + RELAY.write_bytes((b"\xef\xbb\xbf" if bom else b"") + text.encode("utf-8")) + print(" ✓ 写回 relay.py") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/_build/add_update_test_stage.py b/_build/add_update_test_stage.py new file mode 100644 index 0000000..093a86d --- /dev/null +++ b/_build/add_update_test_stage.py @@ -0,0 +1,94 @@ +# -*- coding: utf-8 -*- +r'''往 _tests/verify_all.ps1 里加两个关卡。 + +== 加哪两个 == + + 3a10 静默自动更新链路(_tests/test_update_flow.py) + 3a11 远程鼠标 + 显示桌面(_tests/test_mouse_control.py) + +== 这个脚本自己踩过的坑,写下来免得再犯 == + +往 server.py 插方法时,我两次把 **dispatch 的兜底 return** +一起卷进了新方法里: + + if cmd == P.CMD_XXX: + return self._do_xxx(msg) + + return {"ok": False, "error": f"未知命令: {cmd}"} <- dispatch 的兜底 + + def _do_xxx(...): <- 我的新方法 + ... + return {"ok": False, "error": f"未知命令: {cmd}"} <- 被卷进来了! + + 结果 pyflakes 报 "undefined name 'cmd'",而 dispatch 丢了兜底分支。 + + 这次改 PowerShell 里插关卡用**更笨但更稳**的办法: + 直接在固定锚点**之前**插,而且插完立刻读回来看一眼结构。 +''' +from __future__ import annotations + +import sys +from pathlib import Path + +ROOT = Path(__file__).resolve().parent.parent +VERIFY = ROOT / "_tests" / "verify_all.ps1" + +STAGES = ''' +# ---------------------------------------------------------------- 3a10 +Banner "3a10/7 静默自动更新链路" +# 用户要求:"手表端点击一个更新按钮,电脑端就可以自动进行静默更新, +# 其他什么设置都不变 …… 不弹任何窗口出来" +# 这条链是:中转 /v1/update/latest -> 被控端 updater.py 下载校验 +# -> 分离 .cmd 帮手 -> 静默装 -> 重启。 +# +# 这里验协议常量、版本比较(按数字段,不是字符串)、下载校验、 +# 以及**服务端 dispatch 的结构没被补丁弄坏**。 +$stageOut = & python (Join-Path $root "_tests\\test_update_flow.py") 2>&1 + Show-Fails $stageOut +Record "静默自动更新" ($LASTEXITCODE -eq 0) + +# ---------------------------------------------------------------- 3a11 +Banner "3a11/7 远程鼠标 + 一键显示桌面" +# 鼠标注入是最"有力量"的功能,参数校验写错的话表现是"手表点了没反应", +# 教室里没法排查。这里盯坐标归一化(除以 宽-1 那条规则)、 +# 滚轮夹取、Win+D 的事件顺序、以及非法参数必须被拒。 +$stageOut = & python (Join-Path $root "_tests\\test_mouse_control.py") 2>&1 + Show-Fails $stageOut +Record "远程鼠标控制" ($LASTEXITCODE -eq 0) + +''' + +ANCHOR = '# ---------------------------------------------------------------- 3a5' + + +def main() -> int: + raw = VERIFY.read_bytes() + bom = raw.startswith(b"\xef\xbb\xbf") + text = raw.decode("utf-8-sig") + + if "静默自动更新链路" in text: + print(" 已经加过了") + return 0 + + if ANCHOR not in text: + print(" ✗ 找不到锚点 %r" % ANCHOR) + return 1 + print(" ✓ 锚点就位") + + text = text.replace(ANCHOR, STAGES.lstrip("\n") + "\n" + ANCHOR, 1) + VERIFY.write_bytes((b"\xef\xbb\xbf" if bom else b"") + text.encode("utf-8")) + print(" ✓ 加了 3a10 / 3a11 两个关卡") + + # 读回来确认结构:两个 Record 都在、锚点还在 + back = VERIFY.read_text(encoding="utf-8-sig") + for token in ('Record "静默自动更新"', 'Record "远程鼠标控制"', ANCHOR): + ok = token in back + print(" [%s] 读回确认:%s" % ("PASS" if ok else "FAIL", + token[:40])) + if not ok: + return 1 + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/_build/check_1panel_conflict.py b/_build/check_1panel_conflict.py new file mode 100644 index 0000000..23b7807 --- /dev/null +++ b/_build/check_1panel_conflict.py @@ -0,0 +1,93 @@ +# -*- coding: utf-8 -*- +r'''查 1Panel 和我装的 nginx 会不会打架。 + +== 为什么要查 == + +发现服务器上有 1Panel。1Panel 默认会**自己装一套 OpenResty(nginx)** +放在 /opt/1panel/apps/openresty/ 下,并且**接管 80/443**。 + +而我为了给遥控中转做反代,用 apt 装了**系统 nginx**,也在听 80/443。 + +两个 nginx 抢同一组端口,只有一个能起来 —— 另一个会 +`Address already in use`。所以现在实际生效的到底是谁的配置, +必须搞清楚,否则: + + * 我给 yk.xxmxxs.top 配的证书和反代可能**根本没生效** + (之前测通了,说明生效的是我的那份) + * 但用户在 1Panel 里建站,可能**建了个寂寞**(1Panel 的 OpenResty 起不来) + +== 查什么 == + + 1. 系统里到底有几个 nginx/OpenResty 可执行文件 + 2. 现在听 80/443 的是哪个进程(看 exe 路径) + 3. 1Panel 的应用目录里有没有 openresty、有没有建过站 + 4. 1Panel 面板自己在哪个端口 + 5. 有没有已经存在的 WordPress 容器 +''' +from __future__ import annotations + +import sys +from pathlib import Path + +ROOT = Path(__file__).resolve().parent.parent +sys.path.insert(0, str(ROOT / "_build")) +from lssh import run # noqa: E402 + +REMOTE = r''' +echo "==== 1. 有几个 nginx / openresty ====" +for p in /usr/sbin/nginx /usr/local/openresty/nginx/sbin/nginx \ + /opt/1panel/apps/openresty/openresty/bin/openresty; do + [ -x "$p" ] && echo " 有: $p" +done +command -v nginx openresty 2>/dev/null | sed 's/^/ PATH 里: /' + +echo +echo "==== 2. 现在听 80/443 的是谁(看可执行文件路径)====" +sudo ss -tlnp 2>/dev/null | grep -E ':(80|443) ' | sed 's/^/ /' + +echo +echo "==== 3. 1Panel 的 nginx 有没有在跑 ====" +for s in openresty nginx 1panel; do + st=$(systemctl is-active $s 2>/dev/null) + [ -n "$st" ] && echo " $s: $st" +done +echo " --- 1Panel 应用目录 ---" +sudo ls -1 /opt/1panel/apps/ 2>/dev/null | sed 's/^/ /' || echo " (没有 /opt/1panel/apps)" +echo " --- 1Panel 的 www 站点目录 ---" +sudo ls -1 /opt/1panel/www/sites/ 2>/dev/null | sed 's/^/ /' || echo " (还没建过站)" + +echo +echo "==== 4. 1Panel 面板自己在哪 ====" +sudo ss -tlnp 2>/dev/null | grep -i 1panel | sed 's/^/ /' +sudo grep -h "Port\|port" /opt/1panel/conf/app.yaml 2>/dev/null | head -3 | sed 's/^/ /' + +echo +echo "==== 5. 现有的容器 ====" +sudo docker ps --format ' {{.Names}} {{.Image}} {{.Status}} {{.Ports}}' 2>/dev/null || echo " (docker 命令不可用)" + +echo +echo "==== 6. 系统 nginx 的配置来自哪 ====" +systemctl show nginx -p FragmentPath 2>/dev/null | sed 's/^/ /' +nginx -v 2>&1 | sed 's/^/ /' + +echo +echo "==== 7. 1Panel 有没有装 php/mysql 应用 ====" +sudo ls -1 /opt/1panel/apps/ 2>/dev/null | while read a; do + echo " 应用 $a:" + sudo ls -1 /opt/1panel/apps/$a 2>/dev/null | sed 's/^/ /' +done +''' + + +def main() -> int: + print("=" * 58) + print(" 1Panel 和系统 nginx 会不会打架") + print("=" * 58) + print() + out = run(REMOTE, timeout=240, quiet=True) + print(out[1] if isinstance(out, tuple) else out) + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/_build/check_server_detail.py b/_build/check_server_detail.py new file mode 100644 index 0000000..dc6154f --- /dev/null +++ b/_build/check_server_detail.py @@ -0,0 +1,80 @@ +# -*- coding: utf-8 -*- +r'''把上次查询里两个可疑点查清楚。 + +== 疑点一:证书目录为什么是空的 == + +我明明给 yk.xxmxxs.top 签过 Let's Encrypt 证书。 +但 `/etc/letsencrypt/live/` 列出来是空的 —— +多半是**权限**:那个目录是 0700 root,普通用户 ls 不到。 + +== 疑点二:3306 在监听,但没找到 mariadb/mysql == + +`ss` 显示 127.0.0.1:3306 有人在听,但 `command -v mariadb` 没找到。 +可能是: + * 装了但可执行文件叫别的名字 + * 是别的东西占了 3306(比如某个容器) +得看是哪个进程在听。 + +== 疑点三:内存用量涨了 == + +之前看是 414MB,现在 1037MB。得知道是谁吃的 —— +WordPress + MySQL 还要再吃 300~500MB,这台只有 2GB,得先弄清余量。 + +== 顺带 == + +`grep -r /etc/nginx/sites-enabled/` 没输出 server_name, +是因为那里全是**指向 sites-available 的符号链接**, +而 `grep -r` **默认不跟符号链接**。不是配置丢了。 +''' +from __future__ import annotations + +import sys +from pathlib import Path + +ROOT = Path(__file__).resolve().parent.parent +sys.path.insert(0, str(ROOT / "_build")) +from lssh import run # noqa: E402 + +REMOTE = r''' +echo "==== 1. 证书(带 sudo)====" +sudo ls -1 /etc/letsencrypt/live/ 2>&1 | sed 's/^/ /' +echo " --- yk.xxmxxs.top 的证书 ---" +sudo openssl x509 -in /etc/letsencrypt/live/yk.xxmxxs.top/fullchain.pem \ + -noout -subject -dates 2>&1 | sed 's/^/ /' +echo +echo "==== 2. nginx 站点(跟符号链接)====" +sudo grep -h server_name /etc/nginx/sites-available/ykxmz 2>/dev/null | sed 's/^/ /' +echo " sites-enabled:" +sudo ls -la /etc/nginx/sites-enabled/ 2>&1 | tail -n +2 | sed 's/^/ /' +echo +echo "==== 3. 谁在听 3306 ====" +sudo ss -tlnp 2>/dev/null | grep 3306 | sed 's/^/ /' +echo +echo "==== 4. 数据库到底装没装 ====" +dpkg -l 2>/dev/null | grep -iE 'mariadb|mysql' | head -8 | sed 's/^/ /' +echo " (上面没输出就是没装 —— 那 3306 是别的东西占的)" +echo +echo "==== 5. 内存被谁吃了 ====" +ps -eo rss,comm --sort=-rss 2>/dev/null | head -10 | sed 's/^/ /' +echo +echo " --- systemd 服务占用 ---" +systemd-cgtop -n 1 -b 2>/dev/null | head -8 | sed 's/^/ /' +echo +echo "==== 6. 有没有 swap ====" +free -m | sed 's/^/ /' +swapon --show 2>/dev/null | sed 's/^/ /' || echo " 没有 swap" +''' + + +def main() -> int: + print("=" * 58) + print(" 查清三个疑点") + print("=" * 58) + print() + out = run(REMOTE, timeout=240, quiet=True) + print(out[1] if isinstance(out, tuple) else out) + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/_build/check_wp_ready.py b/_build/check_wp_ready.py new file mode 100644 index 0000000..b4125c8 --- /dev/null +++ b/_build/check_wp_ready.py @@ -0,0 +1,83 @@ +# -*- coding: utf-8 -*- +r'''看服务器上装 WordPress 缺什么。 + +== 背景 == + +用户想把 xxmxxs.top 拿来做 WordPress(原来是 Cloudflare Workers 占着)。 +WordPress 要 PHP + 数据库,而服务器上现在只跑着 nginx(给遥控中转用)。 + +== 为什么写成文件 == + +这个查询里有一堆 `$(...)`、`$b`、`2>/dev/null`。 +用 `python -c "..."` 传的话,**PowerShell 会先在本机把它们求值** +(报"无法将 systemctl 项识别为 cmdlet"、"未能找到路径 C:\dev\null")。 +这个坑今天踩了太多次 —— 凡是超过两行的远端命令,一律写成文件。 +''' +from __future__ import annotations + +import sys +from pathlib import Path + +ROOT = Path(__file__).resolve().parent.parent +sys.path.insert(0, str(ROOT / "_build")) +from lssh import run # noqa: E402 + +REMOTE = r''' +echo "==== 磁盘 ====" +df -h / | tail -1 +echo +echo "==== 内存 ====" +free -m | head -2 +echo +echo "==== nginx 站点 ====" +ls -1 /etc/nginx/sites-enabled/ 2>/dev/null +echo " server_name:" +grep -rh server_name /etc/nginx/sites-enabled/ 2>/dev/null | sed 's/^/ /' +echo +echo "==== PHP ====" +if command -v php >/dev/null 2>&1; then + php -v | head -1 + echo " php-fpm: $(systemctl is-active php8.3-fpm 2>/dev/null || systemctl is-active php8.2-fpm 2>/dev/null || echo 没装)" +else + echo " 没装 PHP" +fi +echo +echo "==== 数据库 ====" +if command -v mariadb >/dev/null 2>&1; then + echo " mariadb 客户端在: $(command -v mariadb)" + echo " mariadb 服务: $(systemctl is-active mariadb 2>/dev/null)" +elif command -v mysql >/dev/null 2>&1; then + echo " mysql 客户端在" + echo " mysql 服务: $(systemctl is-active mysql 2>/dev/null)" +else + echo " 没装数据库" +fi +echo +echo "==== apt 源里有 php-fpm 吗 ====" +apt-cache policy php-fpm 2>/dev/null | head -3 | sed 's/^/ /' +echo +echo "==== 服务状态 ====" +for s in nginx ykxmz-relay ykxmz-monitor.timer certbot.timer; do + printf ' %-24s %s\n' "$s" "$(systemctl is-active $s 2>&1)" +done +echo +echo "==== 证书目录 ====" +ls -1 /etc/letsencrypt/live/ 2>/dev/null | sed 's/^/ /' || echo " (空)" +echo +echo "==== 监听端口 ====" +ss -tln 2>/dev/null | grep -E ':(80|443|3306) ' | sed 's/^/ /' +''' + + +def main() -> int: + print("=" * 58) + print(" 服务器 WordPress 就绪度") + print("=" * 58) + print() + out = run(REMOTE, timeout=240, quiet=True) + print(out[1] if isinstance(out, tuple) else out) + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/_build/commit_msg_216.txt b/_build/commit_msg_216.txt new file mode 100644 index 0000000..b3fd0f7 --- /dev/null +++ b/_build/commit_msg_216.txt @@ -0,0 +1,106 @@ +2.1.6:静默自动更新 + 远程鼠标控制 + 一键显示桌面 + +用户这次提了一整套升级。这个提交先落地三块(其余几块在做)。 + +== 一、静默自动更新 == + +用户原话:"手表端点击一个更新按钮,电脑端就可以自动进行静默更新, +其他什么设置都不变 …… 不弹任何窗口出来"。 + +链路: + + 手表点按钮 -> 被控端问中转"最新是哪版" + -> 下载到 %TEMP%\ykxmz-update\ + -> 校验 sha256 + -> 起一个**分离的 .cmd 帮手** + -> 自己退出 + -> 帮手等它退出 -> 静默跑安装器 -> 重新拉起 + +关键设计说明: + + * **为什么要有那个 .cmd 帮手**:Windows 上正在运行的 exe 是锁着的, + 安装器覆盖不了,所以必须"先退出、再装、再起来",而这三步 + 不能由被控端自己做(它一退出就没人执行后面的了)。 + 用 DETACHED_PROCESS 起的助手不受父进程退出影响。 + + * **配置为什么不会丢**:配置在 %ProgramData%\ykxmz\data\, + **不在程序目录里**。安装器升级只替换 {app} 下的文件, + 不碰 ProgramData —— 中转地址、配对码、各种开关原样保留。 + 这是这个功能能做的前提。 + + * **更新源是用户自己的中转站**,不是作者的服务器,也不是任何 + 写死的公网地址。被控端问的是它自己配置的那个地址。 + 中转没有这个接口(老版本服务端)时会明确提示"先手动升级一次服务端"。 + + * **sha256 必须校验**:自更新最大的风险是下到一半断了或被换了, + 装上去是个坏程序、而且它还会自己再更新 —— 螺旋下去很难救。 + 对不上就**放弃这一轮**,保持现状(现状至少能用)。 + +中转新增:GET /v1/update/latest?platform=windows +返回 version / name / url / size / sha256。版本比较按**数字段** +(不能按字符串,否则 "1.9.0" > "2.1.4")。sha256 按 +(文件名,大小,mtime) 缓存,避免每次重算 9MB。 + +手表端:"电脑端版本:点这里查看" 按钮,查完显示 +"电脑端 2.1.4 → 2.1.5(点这里更新)",确认后才真的更新。 + +== 二、远程鼠标控制 == + +新增 windows/lanremote/input.py,用 SendInput 模拟鼠标键盘。 + + * **为什么用 SendInput 而不是 mouse_event**:后者是 Win95 时代的 + API,会被 UIPI 拦,而且没有 dwExtraInfo(多个注入源会互相干扰)。 + SendInput 还能一次打包多个事件,按下+抬起之间不会插进别的输入。 + + * **坐标是 0..65535 归一化**,而且**必须除以 (屏幕宽-1)**。 + 少了那个 -1,最右边和最下边几像素点不到 —— 表现为 + "鼠标到不了屏幕边缘",任务栏在底下的机器上尤其明显。 + + * **INPUT 结构在 32/64 位下大小不同**(28 / 40), + ctypes 里按进程位数算,不能写死。 + + * 权限说明:目标窗口如果是管理员权限的(UAC 提升过的), + 普通权限的注入会被 UIPI 拦掉。这不是 bug,是 Windows 的设计。 + 被控端以管理员运行就都能点。 + +== 三、一键显示桌面 == + +用户要求快捷栏里那个"不能改"的按钮就是这个功能,等于 Win+D。 +和鼠标控制共用 input.py。 + +== 顺手修的一个自己造的坑 == + +往 server.py 插方法时,**两次**把 dispatch 的兜底 +`return {"ok": False, "error": f"未知命令: {cmd}"}` 一起卷进了新方法里: + + * pyflakes 报 "undefined name 'cmd'" + * 而且 dispatch 丢了兜底分支(未知命令会返回 None) + +第一次靠 pyflakes 抓到,第二次也是。为了以后不再犯, +`_tests/test_update_flow.py` 里加了**结构断言**: +兜底必须在带 CMD_UPDATE 的那个方法体里,而各新方法里不能出现它。 + +(那条断言第一版写得太死 —— 要求"紧跟 CMD_UPDATE 分支", + 后来往 dispatch 里插了新分支就误报了。改成验结构真相。) + +== 验证 == + + 全量回归(含新增的 3a10 / 3a11 两关)全部 PASS + 静默自动更新:协议 + updater 15 项 + 服务端结构 8 项 + 远程鼠标:坐标归一化 / 滚轮夹取 / Win+D 事件顺序 / 非法参数 22 项 + 610 个 Python 文件语法通过 + 实际注入鼠标成功(move / click / scroll / show_desktop) + +== 还没做的(下一批) == + + 手表顶部快捷栏(可自定义最多 4 个,1 个固定"显示桌面") + 屏幕镜像去掉提示条、去掉所有弹窗 + 托盘图标改成设置项 + 「允许新设备连接」开关(中转后台 + 安卓端) + 弹文字窗口位置/大小可自定义 + 中转后台设备列表显示版本 + 远程点更新 + +== 顺手清理 == + +_tests/out_app/ 下面那堆 .class 是 Java 编译检查的产物, +以前误提交进仓库了(152 个文件)。这次从索引里移除并加进 .gitignore。 diff --git a/_build/package_remote.py b/_build/package_remote.py index 940d59c..1c462cc 100644 --- a/_build/package_remote.py +++ b/_build/package_remote.py @@ -17,7 +17,7 @@ import zipfile from pathlib import Path ROOT = Path(r"C:\Users\shu\Desktop\deepseekagent\LanRemote") -VERSION = "2.1.5" +VERSION = "2.1.6" NAME = f"ykxmz-{VERSION}" AGENT_SRC = ROOT / "dist_exe" / "ykxmz" diff --git a/_build/wire_mouse_cmd.py b/_build/wire_mouse_cmd.py new file mode 100644 index 0000000..d678248 --- /dev/null +++ b/_build/wire_mouse_cmd.py @@ -0,0 +1,162 @@ +# -*- coding: utf-8 -*- +r'''把「远程鼠标控制」和「一键显示桌面」接进协议 + 服务端。 + +== 为什么这两个一起做 == + +它们共用同一个底座(lanremote/input.py 的 SendInput 封装)。 +分开做的话底座要写两遍。 + + {"cmd":"mouse","action":"move","x":800,"y":450} + {"cmd":"mouse","action":"click","button":"left"} + {"cmd":"mouse","action":"click","button":"right","double":true} + {"cmd":"mouse","action":"down","button":"left"} + {"cmd":"mouse","action":"up","button":"left"} + {"cmd":"mouse","action":"scroll","amount":3} (正数向上) + {"cmd":"mouse","action":"scroll","amount":-3,"horizontal":true} + + {"cmd":"show_desktop"} -> Win+D,用户要求的快捷栏固定按钮 + +== 为什么 move 用绝对坐标而不是"相对位移" == + +手表上那块的触摸位置和电脑屏幕不是 1:1 的(表盘才多大)。 +相对位移要手表自己算倍率,算错了鼠标就飘。 +绝对坐标由手表把触摸点按比例映射到 [0,宽]x[0,高], +电脑端只管落到那个像素 —— 责任清晰,也不会累积漂移。 + +== 安全 == + +注入鼠标键盘的能力很强,所以**必须登录配对之后才能用** —— +这一点由现有的握手逻辑保证(没通过配对,一条命令都不执行, +见 _do_pair 和命令分发最前面那道校验)。这里不做额外放松。 +''' +from __future__ import annotations + +import sys +from pathlib import Path + +ROOT = Path(__file__).resolve().parent.parent +PROTO = ROOT / "windows" / "lanremote" / "protocol.py" +SERVER = ROOT / "windows" / "lanremote" / "server.py" + +PROTO_ADD = '''CMD_UPDATE = "update" +# 远程鼠标控制:{"action":"move|click|down|up|scroll", ...} +CMD_MOUSE = "mouse" +# 一键显示桌面(Win+D)。用户要求的快捷栏固定按钮。 +CMD_SHOW_DESKTOP = "show_desktop"''' + + +def main() -> int: + # ---- protocol.py ---- + raw = PROTO.read_bytes() + bom = raw.startswith(b"\xef\xbb\xbf") + text = raw.decode("utf-8-sig") + if "CMD_MOUSE" in text: + print(" protocol.py 已经加过了") + else: + old = 'CMD_UPDATE = "update"' + if old not in text: + print(" ✗ protocol.py 找不到 CMD_UPDATE") + return 1 + text = text.replace(old, PROTO_ADD, 1) + PROTO.write_bytes((b"\xef\xbb\xbf" if bom else b"") + + text.encode("utf-8")) + print(" ✓ protocol.py 加了 CMD_MOUSE / CMD_SHOW_DESKTOP") + + # ---- server.py ---- + raw = SERVER.read_bytes() + bom = raw.startswith(b"\xef\xbb\xbf") + text = raw.decode("utf-8-sig") + if "_do_mouse" in text: + print(" server.py 已经加过了") + return 0 + + anchors = { + "imports": "from . import updater as U", + "update_branch": " if cmd == P.CMD_UPDATE:\n return self._do_update(msg)", + "unknown": ' return {"ok": False, "error": f"未知命令: {cmd}"}', + } + missing = [k for k, v in anchors.items() if v not in text] + if missing: + print(" ✗ server.py 这些锚点找不到:%s" % ", ".join(missing)) + return 1 + print(" ✓ server.py 锚点全部就位") + + text = text.replace(anchors["imports"], + anchors["imports"] + "\nfrom . import input as IN", 1) + + text = text.replace( + anchors["update_branch"], + anchors["update_branch"] + ''' + + if cmd == P.CMD_MOUSE: + return self._do_mouse(msg) + + if cmd == P.CMD_SHOW_DESKTOP: + try: + IN.show_desktop() + except IN.InputError as exc: + return {"ok": False, "error": str(exc)} + return {"ok": True, "done": "show_desktop"}''', 1) + + handler = ''' + + # ---------------- 远程鼠标 / 键盘注入 ---------------- + + def _do_mouse(self, msg: dict) -> dict: + """处理 {"cmd":"mouse","action":...}。 + + 坐标系是**屏幕像素**(相对虚拟屏幕左上角),由手表按比例映射好 + 再发过来 —— 电脑端不做缩放猜测,免得两边算法不一致导致漂移。 + """ + action = str(msg.get("action") or "").lower() + try: + if action == "move": + # 分辨率可能被改过(投屏切换等),每次移动前刷一下缓存。 + # 不刷的话,插上投影仪之后鼠标会跑到屏幕外。 + IN.refresh_screen() + IN.move_to(int(msg.get("x", 0)), int(msg.get("y", 0))) + return {"ok": True} + + if action == "moveby": + IN.move_by(int(msg.get("dx", 0)), int(msg.get("dy", 0))) + return {"ok": True} + + if action == "click": + IN.click(str(msg.get("button") or "left"), + double=bool(msg.get("double"))) + return {"ok": True} + + if action == "down": + IN.down(str(msg.get("button") or "left")) + return {"ok": True} + + if action == "up": + IN.up(str(msg.get("button") or "left")) + return {"ok": True} + + if action == "scroll": + IN.scroll(int(msg.get("amount", 0)), + horizontal=bool(msg.get("horizontal"))) + return {"ok": True} + + if action == "desktop": + IN.show_desktop() + return {"ok": True} + + return {"ok": False, "error": "不认识的鼠标动作:%s" % action} + except IN.InputError as exc: + return {"ok": False, "error": str(exc)} + except (TypeError, ValueError) as exc: + return {"ok": False, "error": "参数不对:%s" % exc} + +''' + text = text.replace(anchors["unknown"], + handler + "\n" + anchors["unknown"], 1) + + SERVER.write_bytes((b"\xef\xbb\xbf" if bom else b"") + text.encode("utf-8")) + print(" ✓ server.py 加了 _do_mouse") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/_build/wire_update_cmd.py b/_build/wire_update_cmd.py new file mode 100644 index 0000000..df427bf --- /dev/null +++ b/_build/wire_update_cmd.py @@ -0,0 +1,242 @@ +# -*- coding: utf-8 -*- +r'''把「静默自动更新」接进协议和服务端。 + +== 加两条命令 == + + {"cmd":"update","action":"check"} -> 查最新版,不改动任何东西 + {"cmd":"update","action":"start"} -> 真的要更新(下载+静默安装+重启) + +== 为什么"查"和"做"分成两步 == + +手表点一下按钮,先看到"有新版本 2.1.5(当前 2.1.4,约 9 MB)", +再决定要不要升。直接一步到位的话,用户不知道自己触发了什么。 + +== start 为什么要开线程 == + +安装包 9 MB,在学校的网上下载可能要几十秒。 +如果同步下载,手表那边的请求会一直挂着;而挂太久用户以为卡死了。 + +所以:立刻回一句"开始更新了",然后在后台线程里下载 + 校验 + 应用。 +进度通过 server 自己的日志广播给正在看的手表。 + +== 应用之后进程必须退出 == + +Windows 上正在跑的 exe 是锁着的,安装器覆盖不了。 +所以 updater 会先起一个**分离的 .cmd 帮手**(不受本进程退出影响), +帮手等本进程退出 -> 静默装 -> 重新拉起。 +这里的顺序不能反。 +''' +from __future__ import annotations + +import sys +from pathlib import Path + +ROOT = Path(__file__).resolve().parent.parent +PROTO = ROOT / "windows" / "lanremote" / "protocol.py" +SERVER = ROOT / "windows" / "lanremote" / "server.py" + + +def patch_protocol() -> bool: + raw = PROTO.read_bytes() + bom = raw.startswith(b"\xef\xbb\xbf") + text = raw.decode("utf-8-sig") + if "CMD_UPDATE" in text: + print(" protocol.py 已经加过了") + return True + anchor = 'CMD_INFO = "info"' + if anchor not in text: + print(" ✗ protocol.py 找不到 CMD_INFO") + return False + text = text.replace( + anchor, + anchor + "\n" + '# 静默自动更新:从**用户自己的中转站**取新版本包,' + '静默装好并重启。\n' + '# action=check 只看不动;action=start 真的更新。\n' + 'CMD_UPDATE = "update"', 1) + PROTO.write_bytes((b"\xef\xbb\xbf" if bom else b"") + text.encode("utf-8")) + print(" ✓ protocol.py 加了 CMD_UPDATE") + return True + + +def patch_server() -> bool: + raw = SERVER.read_bytes() + bom = raw.startswith(b"\xef\xbb\xbf") + text = raw.decode("utf-8-sig") + if "_do_update" in text: + print(" server.py 已经加过了") + return True + + anchors = { + "info": 'if cmd == P.CMD_INFO:\n return {"ok": True, "info": self.actions.info()}', + "unknown": ' return {"ok": False, "error": f"未知命令: {cmd}"}', + "imports": "from . import protocol as P", + } + missing = [k for k, v in anchors.items() if v not in text] + if missing: + print(" ✗ server.py 这些锚点找不到:%s" % ", ".join(missing)) + return False + print(" ✓ server.py 锚点全部就位") + + # ---- import updater ---- + text = text.replace( + anchors["imports"], + anchors["imports"] + "\nfrom . import updater as U", 1) + + # ---- 命令分发 ---- + text = text.replace( + anchors["info"], + anchors["info"] + ''' + + if cmd == P.CMD_UPDATE: + return self._do_update(msg)''', 1) + + # ---- 实现 ---- + handler = ''' + + # ---------------- 静默自动更新 ---------------- + + def _do_update(self, msg: dict) -> dict: + """处理 {"cmd":"update","action":...}。 + + 取包的地方是**用户自己配的中转站**(store 里的 relay_url), + 不是任何写死的公网地址 —— 这一点是刻意的,见 updater.py 的说明。 + """ + from . import __version__ as my_version + + action = str(msg.get("action") or "check").lower() + relay = (self.store.relay_url() or "").strip() + + if action == "check": + try: + info = U.check(relay, my_version) + except U.UpdateError as exc: + return {"ok": False, "error": str(exc)} + if not info.get("supported"): + return {"ok": False, + "error": info.get("error") or "不支持自动更新"} + if "error" in info: + return {"ok": False, "error": info["error"]} + return { + "ok": True, + "current": my_version, + "latest": info["latest"], + "needUpdate": bool(info["needUpdate"]), + "size": info.get("size", 0), + "name": info.get("name", ""), + } + + if action != "start": + return {"ok": False, "error": "action 只能是 check 或 start"} + + # ---- 真的更新 ---- + # 先查一遍(在调用方线程里查,失败能立刻报回去) + try: + info = U.check(relay, my_version) + except U.UpdateError as exc: + return {"ok": False, "error": str(exc)} + if not info.get("supported") or "error" in info: + return {"ok": False, + "error": info.get("error") or "不支持自动更新"} + if not info["needUpdate"]: + return {"ok": True, "alreadyLatest": True, "current": my_version, + "message": "电脑端已经是最新版本 %s" % my_version} + + # 下载和应用放到后台线程,先回一句让手表知道开始了 + threading.Thread( + target=self._run_update, + args=(info, my_version), + name="ykxmz-update", daemon=True).start() + + return { + "ok": True, + "started": True, + "from": my_version, + "to": info["latest"], + "size": info.get("size", 0), + "message": "正在下载 %s(%.1f MB),下完会自动装好并重启," + "设置和配对码都不变" + % (info["latest"], (info.get("size") or 0) / 1048576.0), + } + + def _run_update(self, info: dict, my_version: str) -> None: + """后台线程:下载 -> 校验 -> 起帮手 -> 自己退出。""" + def log(text: str) -> None: + try: + self._log(text) + except Exception: # noqa: BLE001 + pass + + try: + log("[更新] 开始下载 %s(%.1f MB)" + % (info["latest"], (info.get("size") or 0) / 1048576.0)) + work = Path(tempfile.gettempdir()) / "ykxmz-update" + last = [0] + + def progress(got: int, total: int) -> None: + # 每 25% 报一次,别把日志刷爆 + if total: + pct = int(got * 100 / total) + if pct - last[0] >= 25: + last[0] = pct + log("[更新] 已下载 %d%%(%.1f/%.1f MB)" + % (pct, got / 1048576.0, total / 1048576.0)) + + pkg = U.download(info["url"], work, + info.get("sha256", ""), info.get("size", 0), + progress=progress) + log("[更新] 下载完成并校验通过:%s" % pkg.name) + + # 应用:起分离的 .cmd 帮手,然后**本进程必须退出** + app_exe = Path(sys.executable if getattr(sys, "frozen", False) + else sys.argv[0]).resolve() + logfile = U.apply_update(pkg, app_exe) + log("[更新] 已交给更新帮手(%s),被控端即将退出并重启" % logfile) + U.cleanup_old_downloads() + + # 给日志和网络一点时间落完,再退出 + time.sleep(2) + os._exit(0) + except U.UpdateError as exc: + log("[更新] 失败:%s(保持当前版本 %s)" % (exc, my_version)) + except Exception as exc: # noqa: BLE001 + log("[更新] 意外失败:%s: %s" % (type(exc).__name__, exc)) + +''' + text = text.replace(anchors["unknown"], + handler + "\n" + anchors["unknown"], 1) + + # ---- 补 import ---- + need = [] + head = text.split("class ", 1)[0] + for mod, line in (("tempfile", "import tempfile"), + ("sys", "import sys"), + ("threading", "import threading"), + ("time", "import time"), + ("os", "import os"), + ("Path", "from pathlib import Path")): + if line not in head: + need.append(line) + if need: + anchor = "from . import protocol as P" + text = text.replace(anchor, "\n".join(need) + "\n" + anchor, 1) + print(" ✓ 补了 import: %s" % ", ".join(need)) + + SERVER.write_bytes((b"\xef\xbb\xbf" if bom else b"") + text.encode("utf-8")) + print(" ✓ server.py 加了 _do_update / _run_update") + return True + + +def main() -> int: + print("=" * 58) + print(" 接「静默自动更新」进协议和服务端") + print("=" * 58) + print() + ok = patch_protocol() + print() + ok = patch_server() and ok + return 0 if ok else 1 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/_tests/out_app/com/lanremote/watch/DeviceStore.class b/_tests/out_app/com/lanremote/watch/DeviceStore.class deleted file mode 100644 index c3e145210bffb6364bb363050b28ca3383e143be..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5214 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 S!Si^5e*<(FWkYz;U;7`g8A|*B diff --git a/_tests/out_app/com/lanremote/watch/MainActivity$1.class b/_tests/out_app/com/lanremote/watch/MainActivity$1.class deleted file mode 100644 index 67eafc32eb8208139a7197267a73929da7bbefcf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 806 zcma)4O>Yx15PeQUHoINYH0ArNrKBYZ5}OJn4nasLfD|dEQVYF1j+?k_>}uC7>JQ+@ za0G!G^}r9{e*kAB;D{Kfpdv2IVeOgOnfKm!=Ht(!4*>3>=_7}{heC)`C^9SwGmc`e z6FD|kMlZP)qiBsQ{ZLr7uk1l>m7!=yDy_9ByO)*T4p4&cAqX*pkfE}}_qih&M(h0@ zDJ-GgS6bNz4EcIvtAsKG0#q>Pp&DWyXBZZgiJ|Pi)7uaXY1;n2-&oW(!;hR zKgZyII(++i^6Ja$Bd6j*h>K3bANT*%tG z?!-#G=&6)`%Y}u+v6Z15csfRkl zLWTgs{^td-NSjsy!zHjv#cdlcV2-{ST%qVyvX;nqk)V0&Jy`P)-W&25uF+Yf8~94+ z0wEpe-=XY0MdzFEz^bPwn4KDDCpdpnSe~YQgOBg1;s<%xsU$~aH|Q?VjB8jStNNRq LOp&5Sjvoj2txb8(oG78mJ@f8#8=-@Cdbi!iDZi!u?U)Xe3 zHnyZ26WTpbdGURQRLNd9aRJ>NSqnMzG=;Cmp{F((_^$91x$=hLTB&*#tztJ^on#P$ zi@3zmXQ3Yh45O_mGnm44WfT?XXQ;;cQtKk^s!+8s@M=yFIgiQqWmw@DWEgMt8VOpQ zHIc`VR%n>vZtM3br*ghdE#4K3a$L1gz!*c%c~T7NoggC7`x~`;nkXeUd|9zya*Q+d z{*!gB9tL~b$8nv(ZgNObD?U@MNH**7;yYLF#9p9ItEb9a*HN+8iO@n&lMLNePs!zE zdqak6`aOnRau%DR+(?_a5rsx2{<1wr?plq7`*um}oC2DwK@z(1p?8el*wxdzF5W$ny_KFYoJ2-E1fly2k3GJQzRGX8c0lIKq(e8^bAJ pihLZ#Yfh0!VVaHx9L&(3rh%HpO|k~bqe<#7f3i_YbGV)9_yZ>5B@+Mu diff --git a/_tests/out_app/com/lanremote/watch/MainActivity$100.class b/_tests/out_app/com/lanremote/watch/MainActivity$100.class deleted file mode 100644 index f19477a6ebbd4da6a2d426116445a97ecdebc593..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5449 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 diff --git a/_tests/out_app/com/lanremote/watch/MainActivity$101.class b/_tests/out_app/com/lanremote/watch/MainActivity$101.class deleted file mode 100644 index 4f131b6b6bf2d7ca055abe1fb025cdc98ecc80c7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 942 zcmah|O=}ZD7=9*+*(9BsO|4d~A2D5Rl71wu7KMtUr9z=qFd}%|Y=&gZb~fzJM!kFS zD0mV)2zv1*G;LAQyZ=M&&3_=i(-b14kcFLhX5NqInRlM~@%QsL0N1dTMhtNS365h( zGFXnkRq<>Yh%H}<%2Qi8t;&Y&$~%s7JFa?eEmRj6lB(r~R+XW!J}hBbBcwRe$S|Zj zwr45vOfihDZ`vJOV>K%^6}YmwoB_wEf#V!mm<(n=;6Z&;IFyvU=}K4KVu%+@j}445 zO!R|#RqjTv*APJlIgF-ol7N1{?(V;M_4DP&{q5~vZ@>Tf`bIk^I8NgXm9*P!AsY;H z#X&};fvv;x#B~zq44mh<=aN{yR70&Q&&j&O3g#8JQ$bx--d zGct@9kBptfWsWPDX2=|(Eo!jq#o^a9aMESn;S%+)-{FUmly=<{42c!LK_2FUURF#fh=; w9h{=IVHWprmgYOi@4@O9%xDJuJqr8-A)70qN80=>Px# diff --git a/_tests/out_app/com/lanremote/watch/MainActivity$102.class b/_tests/out_app/com/lanremote/watch/MainActivity$102.class deleted file mode 100644 index 5e60210fa5ea816e466d339289ebf60188d013ce..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 702 zcma)4O=}ZD7=9+ZX4}m)O{4Yuqt*yX1QS0%p(03CD5Msw85oTPt zNIBpfV@NY(mb_&zj?^eE)Rv?$q;^GVWv((<`9gIB8HRGSxe~3ff9XBxKke>r-~YDr zsNa1Vt!+fB52DpKyIb9_z1RJ<$I-(V(X)-uE3d2{&l}{!u$N;WCK*zm5HbvNmoAhk z=H>kGoRvYaMH499n3*e79UNfD4!+#(XzkTnlHv4!NEWIL_N-qg_;^KWd8^Z|$>7d! z0W0We26xf#1VY|avF60zCv!G-CE^V&T7F1*=A~)+b&e^VWJvq^x(bD_HMLOI87A}p z{y^-{=Lizaw!AQuA#v?rZQ2i($;3xacO^mZ7!FeN4$*9y*71&8Jo64#@jXU2X~l4u z#w1nn4>TS@`VT%2E8Yb!zJ-+?&rWP1J7Bo67T-aN_Xu~Ca0wj4ajG`4PLLZp(afew F&rh`4z7PNa diff --git a/_tests/out_app/com/lanremote/watch/MainActivity$103.class b/_tests/out_app/com/lanremote/watch/MainActivity$103.class deleted file mode 100644 index a7026957f190e07b1d18b02b35b5634e8bb88362..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 580 zcmah`+e!m55Ixh@Zrw&(>-{dWLR-OB6cH3byns*@RQkS6gN?LV*i98bOHdRa6!guH z5@%7UZw(}K$(b{goUiZKHvpSh97F<12PuI8qzRr3I+eC>Le&YZs@(ZjwkoH-Hv7`* zTW#;X_38#8ZCg6>s)XfQzk(eX84Nmb1+ox?{EdI>$1Sca=ZzaBEwk-vqwO9cSuE9E z3?t_tFED}vVW}TKA?r)2qR89cs`5lD#r`9e>JG*Th2AI^U1R)4TM<$RL6c3crj0u5 zb{ZXr8+^JFeE%^`%$DKEU`Qp5Jo}yd@K>Gmf)R5fwSZ6Epr@a-16!Z zsQe7)fg{2MpJ}!b?|e=|{NX*eoJFSm2ztR#5ADW0;scKOL=sbsNnjc?Y*VbsEEd>v Jxi;>`>jyWpge3p~ diff --git a/_tests/out_app/com/lanremote/watch/MainActivity$104.class b/_tests/out_app/com/lanremote/watch/MainActivity$104.class deleted file mode 100644 index 44ea6b88ccfd5329804e85ede9a66debc1f76054..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 982 zcma)5O=}ZT6g@AANt#TPG`7}{sxhWINo|wbY6}rTTZKTXV5Q)4GI=CZk~d{$QgP?X z574a(5kUlTkt_slL#?`ZA?Qz#8tbb6K)sI^B1TCE=Dl|>_nmX@$NTtm?LB}=O!?qq zi0NiEUD1TatER)#H#J8ur{^?7T+kh3*>G<4z=J(Lcoq2IXGnC5PmE_K8N5!}uoD@E zc)r_(YybfSeNYtiBE%3`)Rr|Vsg%B2Sme4xXy*)JIOiGMYI4Dc2*Y4IXx_05QMy<& zDn)Ju5Ju3Cy+pjxY;3-(w;nY%*6wVrKWH_ce|z%s$K8jUAD(Wl-}_##$7Jw;f_)ey z3TQE!LQV+1#Vs625?(7LHW*`HB0B0 z4T;(J=TweKR=Rje=oQnZ{?BozY!(%o>oW`?Q_LE+ZVEx;!>OqQ|7t4nD>(|WW-6L( za~m-lby=nqSR^9Ho!*Iy{$89S?nz2d6du8vM+>@Aq*GBahi4y PAx_{doupO|?ezTyP?ZPZ diff --git a/_tests/out_app/com/lanremote/watch/MainActivity$105.class b/_tests/out_app/com/lanremote/watch/MainActivity$105.class deleted file mode 100644 index 740c545ad1d0009ae3f259d5715ec96139b633f6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 580 zcmah`+e!m55Ixh@Zrw&(>-{dWLR-OBP!SYCyns*@RQkS6gN?LV*i98bOHdRa`~aT? z{Tgu=h5FV&GMAh=Gs$`XetiS5fyF^2kaUm|7(klf$)Hnd`zBPKz^clfZ)K};>T9zv zt-jUv-dnG364JJ%Bdx^mvQQPMKot~T245t7AH z-Ni6+4)OvcC=izV@e{JXlq!n6?X4j8N!}a?v%$Z?qL5br3Y!xM!mHnlWY{$X_cG{)QXT6UJM#Z$)?zj;JTkH!MA}@RD%%R00&|!r35a2_}!Oz@dz3-Q6{B*Y)yW6N1&%8#HI4E4}%%p*SGh%Y2D#h3)9F~c^+n#^K> LJ(p|aZoGZ~NlJuf diff --git a/_tests/out_app/com/lanremote/watch/MainActivity$106.class b/_tests/out_app/com/lanremote/watch/MainActivity$106.class deleted file mode 100644 index f3cb5df94b0584e8e9ed692fa26ad7b80c3060e3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1620 zcmbVMOK($06#m9G@q0kCW(!qIH{pcNC>!fpvBZF=>kozN6Ap<-ss-z zL`Yq9QG-rx@FB-X z*xQm|C`H6Zwviy77iP(}R4KWnO0&{UPS|2z=EZfZ;u4kpIN-;}90$?Eu=m}r4DA$L zRF0ZaWok){Th5POhOReJi%Kpfi}cZHqa26PM`6y}VZK$7pDqRJ&{hmb~R z05pS<-yYrGxOr!N<(G|Hch}c`{A+Dl8;3ZC@hJ_8SXhu|iD4kNdCuaSwi@ZFZE2k1 zI1L)HfNRxj)0D+Fhh)9~JnJ3QY)3iHV64eDb6m3>+6LQAII`5j0M0T5TUari<2a8T zU7*I4r*(nMdljgU$d?@X)@zd)PNn_0K*!oV_)8Vj6f}!}L0yTGo;8iK<>c% z71_w=s~BlDkH2MJ#e_Ce%`c`jW={=sAA*GW1%mt~diX2!^K}gK4UF*DxXORW5Bv}O h#Q#KrB7E@lr-{eqTJ$$4*7g{tFio1CYadDa{R?z2v+n=^ diff --git a/_tests/out_app/com/lanremote/watch/MainActivity$107.class b/_tests/out_app/com/lanremote/watch/MainActivity$107.class deleted file mode 100644 index b8106620cc8910d70c99bc28f96c4374e70048f4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 741 zcma))ZEF)j5Xb+Ui@D~qXVcXBklLqseTvYeRS;1GDT+eULR%{I-Da~i+g|p{-Co77 z;ANzRBQ2YNc4sv5eFIEi2-K0-ux}%NS%ZELc9$g=J zX>J(8Lo4KYS6tk9gTo|GrFySj?#y_rwj7;6?~II-OqeAh@O8fqN>#Z diff --git a/_tests/out_app/com/lanremote/watch/MainActivity$108.class b/_tests/out_app/com/lanremote/watch/MainActivity$108.class deleted file mode 100644 index 4ac9c6c12caf1c4735f6aa19404a015a37224a4a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2267 zcma)8-BTM?6#w0P?I!F68iY28iZ!hy6ap0~B&C)j6lwqqlv}H`gc#zQwUNrJrmb&SuAbUbT`iZIR1I@jbB&_m-jDb83^0V;oZ-ZK z7*1!a9mog`vVt%o4B?`hkGs~oZZbr(_tc^)l&z&^UE46%yse4?ezeur^Xh#epg}>S z;L1XPX07TGMA0PS6$Q;`VQ4&-HNA3A*IcskqG1~DB?f;-Vm^XXh)HNw@G9CE+N)X3 zP^W5|?l|$mz8>UL)0;DQRSk*A#6 z%*aSE_^kB^I&n@ymx6Aj7*16ZdCjpXX96phm1j6}VC<2ka!VHy3o?2cf?l@Cc!vQ( z&$|lxaei}X&@XaksfL^>F?TL! zTU+W%UeA;;4_;aYAB5gf1!K5Mt{f!)fKWS7gW5z^HCJuRSWQ_@YMjP-3b=#`hO?ET z`Cmkd_eL;|55!~6GAvhDC}1eFQ0`YP9DxQ?n3nLNg6o)}=#<+|skUi3gd!(P-^EP9 zG`$N=EJ&E6gU6_IxWuqn-TDdvsm*XQYlOg$9&pw$^{K+fif+#diwIh0jWtts3pS1Y zc7YxXpLJDjogSLEL&C@Zg30|vkZ_wedTty(CW`*7Rj@UE%n))?G}@rk1xH zg6v7%%~`7oGy%fg*)phJ%vlGRqD&CT*duw>adbz*U4}%ZWGIEEBeWo>KLif6QvryA zVHSbLMo~xrqEsb3N_D}bRKKXC`KUGH2GMyMohKS3+M4X#0ZX3Sft2jpL0vKy*g<{r zIZiyGRfe0?hlqkN;v*XS&_c6YUQcpZ5wg&Nr!4CqN$;_m98-GsbTvn}abaCdWe0aqSru&(2u{8h> zyaIe8`q0l1QOs&emkmp;nvRlBLf z(GxLSik&#{Bf{V@w*#2K>jI)8Vu&-0c5`{jHVuL(nXOL&9QA@A&{MwuabXr?`zhd#6&9l~V%xXch3d(!yj z&#(5Le)lzpd`rX(s0jUz*|NzHj&?#&jZ7dTVit2$1h!)_EcAA<)9EhAe!Robr&^Y2 z`EiXwY>|6%fB)&DA9$^0MZAZ3Vt!afGrWjqo~wvtqimU4Ic3_Z8?@I|Dkk%=R|O+d0$ebCI`99?Jq&M6BWi>cLK4R0lhzO|3T4p3=3NVaO$2 z&BTfV))p*KW?6taTy94u1eBhcWJkpvD3~=%QF9u{9coYcMSh>==)9rmrcHBkS#>tevWN;c zDY7;-T-K~sDRng2d)Gd9NL(}w)ynF!ZL2oMXm$s|a2wuWkdJVHBn@a+(ielKnTLME z*dSS?-6TmL$#6Vz02ZG-fDlg}U@+dmseL+UxI=3{Nl4%0W7>NVrhlKfD@XW)-f{#z z{s^pz7fA?VoT=lLd?NIkw}}gLXTQaGB9wTHPZIzrBZGp1EIu`M;}7H|f>zc44@9$OF~|ASF5fuLx)DZ&edR6Hm{CF!H; R{|p-KLUhK%$DdqF{0krj4+8)I diff --git a/_tests/out_app/com/lanremote/watch/MainActivity$11.class b/_tests/out_app/com/lanremote/watch/MainActivity$11.class deleted file mode 100644 index c5eacb73954f16fa7e16abb0c9045980123387a0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 904 zcmaizQEw7K5Xb+^0ax&}fJ#fXO53Z#we}$CgQmvBpe7{LK45tAP2JFwF`q_? zyH}p_1NYn$^0lwTT&Qci-Db$EKqPjHVZXOdf2I>`SSX;#urc>SJBSj+Q0ZNIb5Exl zyL}Z4IZl<9h1>dEU%3NX>&rkGcqzrGRA4Una>YS?}8d*5j zAGsX!Pcct|H+(L9uJ6TQbtT=c6M9L)6B90}R+19%zgkeImR3P!BUdWvkpalkK%|!X zpIYj8YH46;hBNfm(vt_|J*T~n)}5mzm~*^@>Fh3X%c*6T*mP>SB`VHW-2Xxv2AkGA zSr~^nBtL^4+C9NjS~cyIehSUsb`}I%5NkoG1%Vc*e8YnqN>S^K2DXeQ3^YRJZnL{Z{RKXo z_@ePq<0B#AVnmFW2NVB?0iXN_#@Q_dg(kSko|)Y<=li}nGxPoT`;P!FBOinZUI9J{ zZSXV16=NZ-$-2oG44bDP$+j|=F3PGtq1fu8YCnl*vl#}zJ*Qf6a{3B~9mqKh0SQ5f z3~h_D7UvAzg?V{VPOCwn5wJxIDgb9s$QB9LEWUt`_VJVvXBHZdr1T z6h;%tgRB9ZVvx2rpKWct`uY6D_R^Ao(+oX(>`i%-4OOdhQ$#O1o#m{AKAa;>a=p&= zD#KvnAoxMP9hh7I=Sf}c$LrAI?xAM-qouvW4by0CZT`(dK9vmT<`tu#29}WCUEHBJN$%-G;8q6^(AW@ZYTa zTBVf~2H%uXCDe|Bs`ERIg$g(CHF<(1wWiBhV8P1L+n zN}^;mOodOYj@Zt>=U~W5hp6(pq8S#oWRcr*MpeQjrl@=ChGpkX(=ZuQEo$AAzb~g` znVM@x*SR^R$(F?}#AtNFU^J)RaM+n3fGo|2YiVF+ks cX#ry-W84kCiCbiS)LIj`O`b?{ouujc11`8afdBvi diff --git a/_tests/out_app/com/lanremote/watch/MainActivity$111.class b/_tests/out_app/com/lanremote/watch/MainActivity$111.class deleted file mode 100644 index eb848f3f27b05e1c8134e5970c509f1aaf0e8175..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 957 zcma)5U2hUW6g^WYyDUpfw^r*nBCUMb0@eD0F)^f#HB?QOkf0A{Stez1*(JL}wLihX zVB#O}%>=bEQQv(w(fF?MR~YY760sq{hn>0i?wxbboO|c<@An@8T*r+lW)PAPRxyhR zL)vt^1>0~u-gN~pJT-*bF6i~`aXb>HoUKd9Fsw~0p5AeKeR0QdS~m9>R{o=QwoYPJ+!kk;uUZbj*Y7sDS2LP6 zMI_f5A~GyxCb43O>%uUfkZz+BithG3lW$wW@r7|=)&u3K$d+T;u21dW<)ZDjR9p*Y zXR+tG)cFeaxak^RtL8p18Ppxe;aQ0B44Pa|jXOS4i=1$i=^7o&JQVDlLU!QV}KE!^CQXo+qG< N*pW$hNMSRg`~iiR@oNA8 diff --git a/_tests/out_app/com/lanremote/watch/MainActivity$112$1.class b/_tests/out_app/com/lanremote/watch/MainActivity$112$1.class deleted file mode 100644 index 2a6fedc95636bf544f8490973b03ac60dd9cc797..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1115 zcmbVLTTc@~6#k}Qw_SGaQk5d!DwbP8+JfAyF);)a641mNAAB2@2`sd`WOs`4&Wn#G zKB$R5Kwk_=tAq!A_kVEFC_lk?riI{zq{K;f&Y3;mIp3Kx=Y09O^$EZYSCyPO&n*d?oWi#p*O9jwntJ;l z>4@WuhO-9Fq3sWNJsyMR1XHCIxmLqJj-nmsHFOxbfQt;NlVpE*-7G_A!|UcFxXjSL z`=YwH{(Sq{yS++f=gsGxm+yDqZtcEa*OA0k4V?zAA;pkv#Lu8}+ZF*8J4^gyy^Rz2 zx2CF0Npu_NQJgIY0Y`?j*$Da=Vt*y8!2JdWaQz58F|O#R|BL>(gfwL6`Hvp=LD_Zr zTv0IO8*`{G%pVt(p?T7qry#Kz#})U=r8(h0tcQ&joCTN5vQO3~zw9!Y1ZDd?1I7rlVGbXLekZ{DB@q82r#U03*%MIHnqpwfk?gBb=9 zvMkl`07;rI8n_UBF=R>RNb6Cvr~ah-t6=GYDzx-Rv~18Xh9TO+Bw=o2nDh`VIuRf3 z%6XYw$%@f*1=s~{1-w23Pl2M1-{MAxCIw|-75j!=!GLnUVJK{vmm&HRRh p`5j&64~&@m7$Fp~BxoD0IgDYPWHXJ>1a6WRry%Vl)lzq(^$R3HBOCw# diff --git a/_tests/out_app/com/lanremote/watch/MainActivity$112.class b/_tests/out_app/com/lanremote/watch/MainActivity$112.class deleted file mode 100644 index 3f7c91fbfc690e2d48ca661830bfb693b1155d82..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1486 zcma)6OHZX0c5avBoJdC7L#~JFxjOj%>8CufSHfSlp5{WvF6@@DN5Nj~W7t&*-H7Md zwAabbQa_`&(&Ytfu~kz+m=^UmuhkM2G|oPkaR`Scv?w@&$bV{IkCGv1=$6HBI9e8R zq&!CmZR9QZ0}(=$0e&HI1qrkhR8P1Jm&;?U1D+ACX-{h#Eu}O?I!jJIfgK8xATs0< zHQhcl-J4=Yw{xyz=8}$=9HP0!T}=W>uB}q5%as{P$w;Bgm)c1Nt$IRV&Xvj{8J}or7%qV|Hu@JA zot^+a^%x}UWtz3n+PBE@#A~ql5~O)rF$~eWj=oSo(R&z5$^R)usiA*Y;tOEQP;1{I zG*uIB%1i~rJ2J~?-j4l?IG9d^Tf;|}5gT3gJN6c-sd)PmPAuZojhARh`}Xkp#4`Hk z{WR!7FFk=`;scN<@n;-VzaXlvphx|QUiCZr)E^j8e_@3DB?KNx=#dG{6Cw(J%1LxX&=gam0*G+Me$2?!DI9d!K&#@$Msl0bFlF9qJ`S6f~fb zA!d1b-7#I?&U>M)-#0^RMV~dfd%+6%Jsv)Y_4V~LG=?iYi1jjbW@>$4$cUmzf~=q! z3PbCvdCwFn?&$N&tF{$Vv=Q#|aFn4wo>-KjqD4Zhf;MOjhik=WXf`d&4uaS~Z!aO5 zjMs*g*w$sC#Cgo&wi_Bz>?Ax^J{QGqhG=km{g%UnP{v;DldxaG0UTu5yB)fjz;o$a zZ+VsUjNi;u6E+fCrb5Ua(ss>gpijGvr7?eSGI^fJ8|eOIx;EkSP{o( zQ(8*vDMFvya_@hSp`~PZ&n8a4#mM!SP&49%Hl8YCazmuUIMTRCkOY7(G!Xz55w!&! zY8$##4Z7NnaW#RodK^<~91Cg^m&uoY0N;_8a8X7*u8^S)3&_$vLcMYoi?q_pTxm&D HP&$4Bs+y-e diff --git a/_tests/out_app/com/lanremote/watch/MainActivity$114.class b/_tests/out_app/com/lanremote/watch/MainActivity$114.class deleted file mode 100644 index a5085b44d15ba7a1140acb55f8373cb26999c1cf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1467 zcma)6+fvg|6kVqjQbGt&FL)gV3$_Ib-I?boNT00uB$j}U4kgca1H zjv;1w>zZS_d3)UpZ0)fbSSwn_b zKw)TJH8)L>Vp*GCTD7f!qK$Ev2NMi6@x+1*6-^SF6|^AAaIso^h6dBJY~PO!^!5^> zsd#lri6fWXEpkpf+;#&!g5!k8TF*srl0n||xMe?bxF5(kh0_wwC^(CA45yC5H|u*Y z?e>;dNzwR2qmr_o*q{>nII;Il!VyUT~b8>9$TSVgyjE9fDmO2rr!!G`~< z7N(oadpxIkzBWyL$Ij~#Xm{sP(pGvgmXe{NPnfHpVWfIOVPL&Xt0DtoyFpx)Fr;7@ zBMi~ML6OuOo=-8OCMpN+`vaDt<3G|Q z7Kn1%%h?P~26yeb!upb(zbDv|x$_0Gcw;s&tu=CRX;#j9g}h}?aj~SOyjDE|1qGUP zEywf8Mj1O;@p1|>;!>#J;MUrLIAbK#jO*I@v}5|d?bC4x(fz@26Rf#e+);E)Li9G^ z7R^plubswXMoxBp1WWEf+M*F%1oW+=7HW#Vx1p5QzoIBL^lv8l0c;nIwOL9}ERa{Vq(5Vh%RGq`PYG6XmU|OBWH0_ZPdM061COk7V3}F^`s11`l49rn4 MTH;1sa(VIj4f%M1&;S4c diff --git a/_tests/out_app/com/lanremote/watch/MainActivity$115.class b/_tests/out_app/com/lanremote/watch/MainActivity$115.class deleted file mode 100644 index 73b530f999cf22f9c1865722e941a601c8b4e53a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1518 zcma)6TT|0O6#fO^?C#lMi>neWCe{- z7+MyL+lELnmz^>K#>{S39S__Pca%@SG^Y(RuzPqp|AjfQDjz8~%B?j}UT zvFecGYr0GoYaX(>X+t$K2>bnm8 z?anR`qp>S`WyxB6*;EL)P3-2iI%)AxWC)ptZ8PkRRfHU^ z%n?RAL!D#fEMIUOQxHd@jJ@N6y~CAZ%CKhwk2`a#u#7`RJ85)?`< zwWU|Y?Oj(_3@7WkJezd=%revhhBV7H~EUJLJ$&rclUDL`3 z+;ze@MG=2ISp5F*@y};(zI}M~{r;05FJ6fT4Ja7I&`N`j91=w8)$1zNR0L7#*>Y8@ zb8GQw3292dqRzjN8TPLuO?;XPA9Ax6L$l5uYrK$~wY-a>W)iMlurKpaE{WyEnTZ^$-$ z-}33q)X*iva0aZUMOk`~+g0Mj}*^!oqWB#`8FH?%W^0zkUaB3u`fi5S9>8F@-2Y z#&kPH+i*PIaRo0vHH6tJZX1?!*A&*CCHk3id5s||T9%(FF{G=L5-JL0#3U#x;!qiq zZDY>}TI?1dHrm`2RPDCqSmF*tIGd{}NMJ@nQpGH^f5LU)S~jH%ycJer9F0L?muzC$o40 z#0bn|S}M7kge=3-q~@6|r`r?v45w*xk740Idgp4y=epbE3^P^B;SYM92KRQ1hD{#H zbp{m~=Cb2ZF-+^iFrN^0!wf}td!ETRtss4Nl$hm!d}^}cn6~TFWp8uQa+@k{2AdP> zdM@3&ZqUuETaLrMb=&ZL?hnkrLm@>wEra1YSTY&xHLZGxzBqE^&6AcUJMbv^!Y8o& z7fA2PVknS|(uu@A$s1T2#_v#-AihxejOo`{%zp%vzCo^swNrXMqRr{`DebIYj~4Vp zoYSrx;%ZPZG$@!rm2>PQ}AI8N%g^Ow^z8h-EDSnQQmwr z#%B{H8sh_r@y)~!Vxm95AAI{IMzQ*KIMxJwL*2xjUHxz?(` zqv9|*cD<(J2tm1?6!+Iu3@~&`Asw#bID@|RdSm;^`>m(Xw%@)}agre_`6H>}G|ng( z)NvL`hJpR=ax?H;BJip!q)z&7dGGk8%ucE)g+od;N-9z`$FIlhKi_O<7{+-8X&o74 z83y+w*sU%@w`JQr2vXz4F_L#SeXzcJ=ywgcBUmZ3sNe#_$U)?(Ik(Z2H!Qd2aGzo5 zKe#iCq~x?$;|#rJ;qrxMy~_RDR@I@5;<+tEhW_+^Q!(_I(y||rXzdQo^qRiSXGPfF zw_BLePxb*ZNbkNg{5z3B|m!vq%XxZE+uh^RN6kviIA&H9O@u;Yjj_6sQS nmng{o zEEV##a;4V(Zu}$H3nF}_MPf2))`7GWGYaqk5ko*%K33`UjX8Dqsy7xNX))pmgRZ8R z=p;PLWagpHu(3oaY53p(w+ zuRsOba*_A|et~~M%(@knC$p0GI&%Y>H$N#T-h~YW6D6m!Za@aW8P7LC-)c(FWefA)In_j%ESz24a`}X$7P0M z5+4T3w4N0EB$%HBW(3@gb(2$~_Y>Gvr1RLhg%Ni^(=;DruLynGoWGR&Um z^+zqkWJkIDb=w`Cd6*qP3b=UWY4_>#rMjV0K|!`8ANV1aPFtwH-?LEHb(kLoK2;GZ zri$2>QUsd=Hw;A>AODI%kvb#?$}Cu+pu2<`Cq>@`R_XQ@?Uw1RZ_H}#2&{Snl=cm>}<0hW!Ti(4o$>_o{Vh`CC|B+(+c z;yM}!r(CMHk(M*5uf3y}3?)65nb%?14p;GyO@iAl9F!UC8IQe7am~;OFZhg`OrxNu zQ>jKd(RFdh5FJB|`9Q>FR8g~0chNwTp?2eTKDZE(CNZz2lKKsU)oS<4xQBHc_gy@| z2E*1Wc?=aEMIy_*qfUnkI%%!0Xj%I5OS^C5A;T_L!!(h@U?#;?(5K5gS7D+?-B?DK zq0F>UB4sFclc8X!hEj>M>0}_%k9-i*i_?=M#q~5LyOP|vGOV}4rQpZyPYmT=GEF1# zK^p)1zq30u&7&;uRTL)~_54)maWZtVYg$`R#phIH<9A{vBVn{LxF?GAbz`1oBD2vV z)>2Z2=6`C!7R|j1hD}(tni)SpNYl-cmW}qWK&OEX!bfE76BY^A{JjO3zrTR(A1qMu zzv1pzdS`e{cZm?z9-fe0K$HHSQZAN9IDnPMtow5cFem>xD&MhI{PNemYzWpNs#ZYc cXL*oq><|`dbv!&Lt3ezi&3v19fp*FH1Hc-=G5`Po diff --git a/_tests/out_app/com/lanremote/watch/MainActivity$120.class b/_tests/out_app/com/lanremote/watch/MainActivity$120.class deleted file mode 100644 index b361deac2d6bca35110fc4a959e1440ceb0cef6a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1504 zcma)6TTj$b5dO|`=~7C81q83$7TgQ6TooyTu!4ZQpsc~5Po-O-SX#2}f$<{Ha}`^4Yg(Z*2d&Kr)OjE2~83ltGv6bTR|6hmSt zTTqu(UScjXIlG{1E@kU8EW_<*$O{CgMaU=>P$r=qK8CvE@-q~xnx;FBzpJf{5RC+m zhZHma6sQihz|eT~()}u=0vT-*+R?!v zW(L;7=hbtfd8t-1WgE$e?Lr+HiTTg`rAcx;xZTDl@k@@B3E))<;uK@ zUIB`PK3rq){f~)evSd3%W_30rQ)$amXH8uR-WM>yP_fIuVMoR$haYSDl55zO7lRDa z&YR7h%@suCNhuEhB@)W*WTs`qH}U<2X!UT9BL2~wOK9!y}8 U-ua}D+n6HDmuZholKJlS7wcw_X#fBK diff --git a/_tests/out_app/com/lanremote/watch/MainActivity$121.class b/_tests/out_app/com/lanremote/watch/MainActivity$121.class deleted file mode 100644 index eef7159b9c023dfa68b7ed704667c1c57e5fd8c3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 922 zcma)4ZEF)j5Pmj^cTGIKi`7(XYhyiYUTQB9!4DBZib5c@LbTsEcT2K0+0(sE#GfU! zD1Pt<_@l(xrqF;o2e$>NzY;H zm%uJ`VCm~vR7y%7OlJ}i9Ne9+U+Y9kJC)5;ejxtH1SYJgJI<(O)$@@)VjTk z?e>kP*fVUetCZ0DW1eLqbMcsAYY8(n>3p%yoFY_7WgZwdkqZJVHh?T?nq;1%&&L+o zDtV8{=Iu+Pc?#CpIECBzhO1xciQzt-4q5oC*d{-RfNl>E(pjbZE~Q$ypm74$JVBYP u`yFc+*(LkmufX@$DE8qQ#tsz)3Z%6fn&hpYqmWB#-o+j@yh%24ihlreMdCF8 diff --git a/_tests/out_app/com/lanremote/watch/MainActivity$122.class b/_tests/out_app/com/lanremote/watch/MainActivity$122.class deleted file mode 100644 index a9565c2a1217680cd4115345a60f8110893c566b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 723 zcmah{U2oGs5S)uc+_(n2Z78J;5Q^KT<|Apu15yzZsz8dA7Kxy~U))o2t$l9oa}YlZ zDG`VVegHoTv6nmq38Yx^&DwjrH?woUe*gFh;3b+>lwjE?ySRl4LlC7&E9NQ_NvcKb zGuP3mb;_lB8)-R~`b%)s=`d9ENajJCVYj!e;Mkn7;-U(N!5i~9$iz({a($9s2nNt-j8r8Xi0Fv1Vi4#Rh5&{$j(2!KH{a@p>(l)NdH9 z=Ha=6P297w<>EfJ85+x&GPpd7M4ksn?M|D-y=pF3*Zr5~;kk`E30TnkSSZB@F{vug zReL?D#95IHMD}T(sP^Shab0Az$;t}F;PlfXi^O|r>el}D;DzZ-!+fWrIL&D?r$Ud? z3m1FXrx{+$=xScI+59g(653TtWG6AtbCIJ#4)|c$f#v(gD1eX*nIYW^x+*1lH?U3k zfTCxFWx`tcbP5*Mr?A84Q>=#H;C-e43=e6o5W?HSBZ^C?(N|-(&O2Bp3qPDtg9+77 lu=*W$7uAkA)7ydP)nVcBtj8{%5SGc<00$H~)Wa~N=MO7zp&9@H diff --git a/_tests/out_app/com/lanremote/watch/MainActivity$123.class b/_tests/out_app/com/lanremote/watch/MainActivity$123.class deleted file mode 100644 index f66a773f0b4204e4511ffe1f36f7f961fa39075e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2273 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 diff --git a/_tests/out_app/com/lanremote/watch/MainActivity$126.class b/_tests/out_app/com/lanremote/watch/MainActivity$126.class deleted file mode 100644 index 890d62089ed2df415ffa6f1cffb7223ff143bd6d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1361 zcma)6*-jHt82-+*Fhdz^sUVA6#j0&t+9HTrTmS(<7Nv&7a5bHt(7|Dbv{Q@^V1jSp zjdvQaOvJE76O30TK8GZ}0}1+{X)BnTV3VHzEZ@I>|9n6AvPbjMgVoafV5JgNzr;09gGlY(-I)hKsbzUfhdlS^AfynWFstP(4omUWN zP)qq&f{r4oqE|#wagj_U6!g(~Z~J4pRtktEQYa$gzOLd1ZjzmHM9D;-h^aX;`Hw^u z3^K5&0_su2AA5UiAE^9yF(hMH#fYewdXzm>)J5*h5MyMF5@`;5I#;wTEn^as#{atp z6-riDWMMcu&)^-gvt-aeWmtTsxSZkn$0ZkaX=BmSoMN6{J^7+VDWn}ue>$zLlo7tP zUCirTFrK0D=wS7Tn91m8+$6EOW2u7$yzr z3N?)CGGSRfKVoWy0xu9*C7KC_d+-JVVg><{G-R%%5ppGsK79=&`nO<=#kw{j$GSIBA8Ye&!XMkl$#r^VxKC>>N%;3ML3;_UbeeQmVdpEn;uSAr8<4*t zNrHVw+mmga4xZV_)ZhW3Y9aOSV-6lnxjNIBA?YPb O&(bZZn8b0C^!p9P3|LkG diff --git a/_tests/out_app/com/lanremote/watch/MainActivity$127.class b/_tests/out_app/com/lanremote/watch/MainActivity$127.class deleted file mode 100644 index 098e7c94303932fd1940d3968c944b232026944a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 554 zcmah`TT22_5dMy9uC80IcE2KGpa!i*kWmC71wmF+_@lRmfegxZhSK?@gaZRM|Iz!_|AN7h-`i_y;tilT>pKtC!v zTS3u_12f;bpKs22|9E`^u#c?_63}!cP0S$0PR4bX{BpUDl;S;CFeF=EpCH$kN?b(a zp73wK9hx602IIzyd`FzgI5z)xG`n#%N<3AL>jhE`IwBl;eG?mB|L#T7?RTW_d;S$y z!X@J8N(sN^@*ofainNI|80Nt&E8YezC_y)i0$CSHDv=&rjM~lO7k8rb2`Gen(EtDd diff --git a/_tests/out_app/com/lanremote/watch/MainActivity$129.class b/_tests/out_app/com/lanremote/watch/MainActivity$129.class deleted file mode 100644 index b5275c7662e22c96508b07428b87baf27d3dfa36..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 828 zcmah{+iuf95IyUJxK0dlOS#|LVyI&f2E?!-@+BO@@^z&wS0xBZl?fA)j)?HHtn| zO7USP;wD^Nv~kHr8yWWI) zxW=%_Ra)dSjiyq}q5&EIKy@>j9Q9#oa$K^AW>4m1KD-)o4%Xah}@P zV%S*rA6)o&z|bluJj>ozTIY)4b+9l^Ul-If-d#wYtugNW6+4sZNa*OaL(JgrDq3AP z<7FvI8v$uPHy3ICXVuuCt8hVUfaQ7SH~{i=gLIY_J#Abke}kfJ@->qQA02~*kB?!8 z-_ZI(DTbS58|2~jaf@OL9=+Yh9kK@eki2Qv48Owuj#Y*^u4%q_132C%BDi~kzDL4< VIyP~iu%P?0PVf|x)NOPc&L1C0%f|o! diff --git a/_tests/out_app/com/lanremote/watch/MainActivity$13.class b/_tests/out_app/com/lanremote/watch/MainActivity$13.class deleted file mode 100644 index bea0ce4e6803e614acd7a3871fa597ca2ef97bde..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1133 zcma)6TTc@~6#k}LyR@uOig*_g3cY}Aix-S2Y6>PKMG|ZJKHU!Oz;-v;9cuUiCcgOS zgKs|g>@6Z_qCdd@(M0})@hl~wYLl{?op0vMnRC8#=A8Zc``ZryH*h-RD4Oa>C8CO=_T;tMtXers0YA2b$&Ja;`=_hjx6UAQoi-Cha15v~n^d`5H zTVjVHQQYKB9(bymCFMz{wittf(>ewW3?j}DKUP?;Yzj+}+q=?{YKcKhXUZ`oFs$Q@ zff1Z#81F@dp`Tlp@cm>ypCjrk>E7HkT~(GvQ(B^=Tu%_`^SGenqJc}e%rMrCMTb6v z5oES0+A@Zb^#6%y$2MwU4C5WF=L6PLCt+Rg!vvXb?R{#!cysXj?ZNYxt@mGlz5CGm z{whdY(!dm^8Dd8XIkdcR0?XaF>PRz~+^Kr5teTeVDB&n`S#sO0t zTSe*9w(uCj1-B{~28+@WkGC5Y;XUOQn72qCmPn4x(Haazsd8jZo>!qMTe>}Rlj3=!yGaIU?&5TiB@iZK o_b{L>VMM!+QSAYFpF1>dB1ages>;*v1cV`a+fNENu@H&=0h>cEk^lez diff --git a/_tests/out_app/com/lanremote/watch/MainActivity$14.class b/_tests/out_app/com/lanremote/watch/MainActivity$14.class deleted file mode 100644 index 17585633fa77d7dbecf3727ad6d5e56c5a2de529..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 651 zcmah`U279T6g@W^v&m+)N$U4!8dpeC=(gCmiXcHjNUfmceKI>FlWt~Ucaq}IDpV?c z@CW###5*V`_8|jvKMrTky>su6pI6@j9HQ4i1wn{vj2oyCo=dw-b72a#v`(dG!pV7h zEVOweonCAAB{?`G)ZAQ`$v&Ys-o!tOP)8#~6r+il&{~MK@D*n1$#kKlW3^Y>X!n{B z^aqm&x3CqW6{C#~p}R?+&=gXtvP=$M?sG>k`kPxCOhViy>? z-19?V)wf3Gw$x^JtlZpYF`oDlwWlkcXCrG|Ve^l|sGJbLH{8!C7iFo+5M5?nE6RfZ z^^Y!Zw+_NKsMYct=A~EoHql}94o5xCJrWIfzk-IB2+uj=U$L#RM=)c15BLA@6ZW28 tA6|gI;pSg<@=hP)DMuZ~{A+)<@eDh)#xJ%{nE3zz diff --git a/_tests/out_app/com/lanremote/watch/MainActivity$15.class b/_tests/out_app/com/lanremote/watch/MainActivity$15.class deleted file mode 100644 index 8d9a24492a723279a567d78fc2128f1f249de3bb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 929 zcma))+iuf95QhJaL);`TNkdvr1$uBHNlV>InyR-j%o}EuWK70hQ122mdEE{PD=a6Ao4Z@K( zkT&jnGmSd@3x!ZRbGP*BgcZ*@AaaO)>3d}gjBF8W@=0jKWc)(C@ z?DH`?2ABC<$F>|eV@UqRNFa}aKT2|!8{fi zmd;{f$nzi&aqMpIY!Urh^i8T zM)$#KE<(ldpfZKr(h(*4Q!B@<@~eMlBsfu+cWpA~vv@$^HawqUVDE diff --git a/_tests/out_app/com/lanremote/watch/MainActivity$16.class b/_tests/out_app/com/lanremote/watch/MainActivity$16.class deleted file mode 100644 index 7069cd70d73691d0cb00703e9c8ad3f93cf69fb3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 983 zcmb7DZEw>s5PsZ-h9+U{#ybpFwhYp2A|N5Y1RO(!B2s+k^W8nq-PynY{QL#r1@@b$p>D(Sa0Ly9okY*0jH_JCwGq)V zH%S^DaH-xTMlPlK;y-`M&@ibi{AUc?gDU^H1i0#<35UU5^2|?CtqYMegq6X5@6|<}Lp|t@U2NdGjT;_r;ub?tMe-bn(r9%k zlg|uR(C?0I++m2gn&w(gqeLqsl!@L-p6OYiHu)!>2!E)08O01_LC0lb!=SRVxYFaC zQ`{XBo1UHuhSoqT@nJEai2Ng;WMpZMLDG`RzBv;wCpeezEer23#n!Y!lZg?V~3$r)lLt)cudnSQhj_j!BtZ(rZzG;<%|a_%fiSk zD!cceP4W7Q+TF`|QHX-dKZCLnGHg}UPo@1cHgxEsdSJMXTDx7|SAZnF3VJ9t`dhe5 z@*ZhVNY+bBxOV~;?w`O8zr+1XCWia8Hb}y9u}OLjZQ4D+Ls~BfyWuz3n?JB#!$qJ| oB3Wy2tQH|`t-?EaMAE`Gd_o2#sh3`>Z-cBs!y(RH>@}Ky0pGm>XaE2J diff --git a/_tests/out_app/com/lanremote/watch/MainActivity$17.class b/_tests/out_app/com/lanremote/watch/MainActivity$17.class deleted file mode 100644 index 88981c6ca2abc6080ce05b139ee5f4bccd9c6aa4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 620 zcma)3+e!m55Ixh@Zo6Hzt@rz_f>p2ziVt2uL_w$uR{EZ_vdJobmY^s;_yK;D zIExR8FEx;v%;e0O!_3F$+dF_QEaZ_w+Cj!cAF_lcKkU{5p(EK1t*qS(>vw8rLg_=_ zsyk&LDw{imtnDaM*&r;|d*OFodhnM9D$f!w`lE^S#;;3c~lLF_q2j4Hmew(pzEuM^eK^O1F2bO$Vcd z^3TU*thHzblCb;_#%hz0ISQ{CK3G>;p2yvmjIO>3(ow7lZX=8%U!JIhv-Eqy>q%EG zJl1{?8kTb=Z700;Fqh0Pm&}Tp48c3qT1H2KFh&~8^CEK~jDU*8WSP9*6yE~II6Ka- zMUInMG?n+Jdn955`iR)xmk7tiOq$@FDJ6nx+tpfVEb$H#AdG`DnQ)K1+6dniCOJFBuRObjNM_xG298l*xIVFV=wffi)=zHS%2qIYBWYV@;$qUfO?(2t5{ z=|SnmF5HXdg#;`@vSSbIphK&0)9eUGGuw$&QlApS*<2%r zDMIAI69W@Tqzt4j%wU$VIj$2SVLOiS{Y>ew$l8vw-U_!m%m@*UHE@fC!z!iW%7vBo~a+gfKOT*Dke GFFrqC-HzA* diff --git a/_tests/out_app/com/lanremote/watch/MainActivity$2.class b/_tests/out_app/com/lanremote/watch/MainActivity$2.class deleted file mode 100644 index 254a3c3dad201cbc344e4fee95827ad5fc645775..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1400 zcma)6-ER{|5dW=Xe2#lbObP@_AR!pA4dI;dO&~3VP*Us!5kP9+Hs`IgaK5w7>(IRM z!W-h_jaDF@5TYs)RE=Zt)p!1mn4}2AKY$Rk4u&W+3O;#fc6L5~Gdr^{-amd0U^h-R zB7g=1K@%$wV(7BHyybA$7kN(!>mpb7xRv43Jz^_4A=PB8mm#FaWigf_?euc$ecFH6 zL?a>$jT797Nw-i^4C~U|&HA3qS{G$DCzLgqmFkT6P4z`!qRBwBi55f|qUZSp*N}47 z$k=&dlWpjbbfxNNXox4yMbL_G4XiS;8fzHZmT=2pP!t`2V#gDoqAQZrV)HRa)MBqS zu@2ucgq1g{eCg&GR*}JH81qunZN~-!8%=a*^#wTVW-e_A~r=un5WQ89=51PW*!_czOhNA0}eJ1wf0NpGv6oi{)=#KwSQeTKZ z`lhSs!CDRYkcqRn-bt`e@Yj!Eu#xgpy4zj9l|M%)2M+85HYNXNow z=>M9!*S#)o;lw$H;DDDElxmKTJE;T|sAe8C!oR&$Mk#RQ>Ck~% z=#4OlpG7m~um=BO170D4*XYF?r12KV>6G5JcSsm$?;pKtC!v zOAksfHZXH8^PTT}ob&bl@d@A*dohF%)}fi0Lxf@9c84X$k{%zrikBWNW%o->OUNr* ziLp>mxsx(OMD>NABPZLKg|8ThBBsMI5r@f;99Uy3=+G{SYF9V-m!$;aJD zN~_~=hJ$}F7TP4g>UIg9Xb8z~M#B#G?tclw-bgYSEqCPEye%4MrCY#zag$gLy3__o|NN``kx{b{GZ2GFUPh?2%R*q8rC5W!K2dQXFKA z;^8Y;@eTSjMGWioM#w^|(Ypb23Qt+WzksFZ-;wx>uOp02LWHn|ZL%6|YloCj4RHj$ G===avZ;sdi diff --git a/_tests/out_app/com/lanremote/watch/MainActivity$21.class b/_tests/out_app/com/lanremote/watch/MainActivity$21.class deleted file mode 100644 index c689fcbab6290b568c7c2992943bb1583099c872..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 616 zcma)3+e*Vg5Ixgd+B9lg@As+|tb(ng_@D?<6osl_rSEB6?3Q#R$)@6G35w!_AK*uc z6MSg#r7rBu&g_{phncVMk52%5SjixUxPgR)BvOPm$Lm&HTlu2vX;FE!wbQO#+ESf3 zTK1%VD(&tNQo1dJQkAe;ABI0Pkw(UVX(0=Xkn7kzJL+&>xo&lYqnYhUDyfeN@r`mb zjZs3f=egm7i5&6<3KqsOK`0JuMabHYBZ8o`vtMO#2OGoH)%$W9+L!A7u-r5-MJV(? zZbGH(mMaMB|6nXP35l9_$MCVbRN^Y^wuFECOAz-%MKBv)=sV&}Mx6P<>260|x$smu zt{1SJOQGA|orUFScIjwN%oGr;3#Ei#bL}7y0gAlDGzgQRTrOH9?>5Fai)qfz@N1Rh zC}VDJy@EF1U_5g~m}Q${kHk6KIamXD&NBW5R2X^3*kAkw;+SVd3=3FfpWtmRu@bIf Ij;I%(A68tCR{#J2 diff --git a/_tests/out_app/com/lanremote/watch/MainActivity$22.class b/_tests/out_app/com/lanremote/watch/MainActivity$22.class deleted file mode 100644 index 4248b0f1b0769133a1ab976e997a73432122be6c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 618 zcma)3T}uK{5Iv*j>bm7e-`{2iX^ZzPP^{Gmaov+au}Ua3#^LKG5=a^_Eu>%(GWYh-4m)%!H|=}jXl6T-O6p@mbi33_ zV2WT2J-6eHl!*+o267grF+<3YYeq=fjw6DgxOY%xd57EM^)-G3HMK8Qw_a))m?PwV zK5hp}*=<)4w*J9bY7t_mUWei7hE(Eu&~FR>?wcU$4-~;{dIR4P=Q8BX{+{n{*p&;< zl;e5<%efM|=XETshto@hlVYZTU|lFB{8QHs0udn3YfOMJ3(91|Rq}Qtd{da`>;k_E z9ETaRyz>kyzrc9ph_J{u&K|K#wo9;n;W^9rCs1zk73shD2BKJILblix-``dSS&;Tc4@MAD5U4?h@9TESD|#1p?;8EApeTCi2lS(& zS$Z&fu?u(R&fGI+4l|!$?;ik;u%1Q?aRUhpV@MJEwkUeuKfFO=K! z0+v%3y5rqhSc^uNipIoD0l~UbO8C{D69ghao_Cl6VFqN|(H?oVF}@khadw_x8yrU& zb7%JjwDSt%i6g=S+a!A=8f+I~{lasW@z0>#_!}nw;;#|M5+h<*#tQocZ)=s6a1C=r Hz4&|sS3{2J diff --git a/_tests/out_app/com/lanremote/watch/MainActivity$24.class b/_tests/out_app/com/lanremote/watch/MainActivity$24.class deleted file mode 100644 index 13aac1f740b274912014a0d65d55e949e9f395dd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 750 zcma)4+iDY06kR(RGt|2>k56uaaGcxB)#1HVn z7oU9f#Xk@gi-I5Ef2jBq;y$g=qJ=sz=dxL6t-bc%Uw?i20$>TPDddoM;RHB_0%0yn z#+^7+sTe0lbe@JL8g^DfsqREZPNaF(xUobin4#2-MMAxI6u#@j!;}j@KoJ3Jqp%~s#T3}HHo6D>9;QfFbTeUwpgQ4Me$CkV5LNj}htVuEBa zB2=2K-mXe!WX#Dw>3TRt2);dk_xJyyhWQ*aY zo>bytHXev{eUBiYW?UnGEy>bI+>@5Ga?s;7tI7}WDqDWecSV?CvK8Q}ZJcMD#7vqi zd0#1!F2|wPLZikLom2p#>8e1D1^}Y=->69l5GP&$;XjxEE8lBOyh6%%D|kZ3|P# z8OYlxpvX`fm!Bc)dLECW%Fe+yb+l0(-%_n@V1}Xi+saiUrQ7j2gWd?b#Bxm``9(79 z@bKo_p@oTLu&#q7^!S-j*uvk%Y^iJ%cq%TUfqDslA@nQk*u>Ml}T3?tG@XRR)2x|rf~6f^_6jR~X~*1cfp_^u53Fo?Nx@5Wx=X}Ln4c(E9X z_@T19&ybG$BC2dNl$+!5hZam^3|Ka@uo?0LcjT%HJ?FAB;9gAGj)WBPF@sjEwJl5` zXCQB*fFeU_Tz-bE>v=qiDmw?;)X_$Dd`q>qff*|0Jms(Q~&?~ diff --git a/_tests/out_app/com/lanremote/watch/MainActivity$27.class b/_tests/out_app/com/lanremote/watch/MainActivity$27.class deleted file mode 100644 index 95e4a6581c8f43596f544fdec10d2dc275bc9bf3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 622 zcma)3+e!m55IyOx-MVhIt@rz_g00}H2r7yoMWIj?RQjsv8rn#kh26B`X9s{w>!*!$0Oy9Ryl-q`vEg zjUER{4-Qfci{0?~jV*?xZpb*l#;{Q7hTnDJqR)ZnA&q{9%nk2w(^1_&t=)(~6Wg9t zQtvZZ%Y|wR83sqTcwP8K7YsQMgBW7y<3S+eIKRHLM#9_6-IdFgAFXp8Nmbu1R2_^E z=g-GkTPa>^3WlYBP!_5Tb}76j_&`M}anf$pM0D{*U`1`k;GT!=C=f@|aAtpxdex{> z;h_qeVNC5D3*88>JN{Oh{UDu!>LUy{Cv_oZci^*q8K9p#K}c1j}TRE;pkY|&n^9kUVkq7_) diff --git a/_tests/out_app/com/lanremote/watch/MainActivity$28.class b/_tests/out_app/com/lanremote/watch/MainActivity$28.class deleted file mode 100644 index 8631a54fda9dfe5debc56cadb16cd5c6e1d8d893..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 622 zcma)3+e!m55IyOx-MVhI^?turuoYYt9~4CpQ4p$vmA`iZ3;g%q3^e8D`!-US0ugVZlR&VO~U?u+4QWJCTv$EjOYSo^quRgi$xj-1Wjm zkB4jk4>^XVZurvXHbd65R8m@JSgdx#@A~l3=OGA?M?XX1n%{8S(N%cfxR$~Y+n&@zqk<$4YU29Kvr6@?Wa41)oNFwD@$g^)>7+Spkq;q8^~$|uzyt(u9IzS=F%O5Qr&Z2u`$?@nM@MiA*q08?u8K0V@>jmT0{hx_OLIcAUIL zifzWPtUki2JfZiVB8CZivt;2m=$%CWH+~t8%_3iUfKznd2M3-}{97UjG)15crZGd- PrIJ}{hGfWd$H$CJU!91d*1GGUlH4( zkU||X7{yX6fixv$w`_wU>tG7g3=^L3^C-&iR(44Apg3B!-uThERH2a9)l$pCEOGuk zTnwf3+I`MY_y=XF#bDNg4#6iILh`fWpv}XpF9IVRN(TEf7=}JS6`C{i+vaVpN{uJd z?*|c$)8wifbR4W{Qxe($Vu~1?x|BSu^}Q(K5puL0ZNv;%I;}TEtBuj0#2i)UDchjC zuGr6Gt zuq2xJ1B^bKm>92#mzekujAu$C;3crh&di?u&UeoF=FB|*`Q#aZlbCKo9YPkuHtNwp z=yOylQ52ppOG?YclF&{ekrA#p=4iL<>gDJOLW3^2L3EhW>CNED2pZ93A!4H$Hlgjd zC<_x{K9QZfEgj8lBd+J_3xrTCo{L}`S}nBMXh#R3TX=b2x%oudl}m|?(ysC*%hF@F z)_JK-Qp(GS;Ev2!{ZkQiVuyt;8#~cWXy4$UV-kWTJhvpYBpiyx(|-Y`eYGHcy*yS$ zDW5CYjXf6j+Sq3*=-t$VOLBzg$YS{7!o63IhsoTO?!r*^W z&z7Yh6qMSKSa{Ta@iKxl7%`1_o)G38$XU4fuMLf6y&1LW6w+>>r6+yUDVukCJjbl# zD$kA8nsz;TeX%qr{TVS=WQ(O%DG9@C0#%#ZG-dL)W`Fj1GJcEDazhK}PDU)$$Ran? zqVLEl*D$xQKb=G7cyatm&nc?F_2x5D7gXNDRYK2ZXVpd;8Ez(_Ib31dmq9@2`d7N9 zR6FIdG~-1P1d?&Be`tmtzE~PTxBzNvGnWfDZ5_YO7-j1iulo4gSR(xck3sz_upaRj z;S!GxJcC~IIF5-L{sqDa@xSH43R)iUf}db@s@i*aeXVA@Hc-L#3U-;PV}At)%y6(1 zhbxFZ#o+7;hAK!@aN<4&)Rk(SC>U^T z6j5R*Ps`7c_c#|(RNZejsiU3R^p@&f0}BkL-&U>@rM#Xm81z=qCst@nB`%Y3PlUJM z4lPU+gLM-mAs6Q|#TNfAW-rY~foF>QK_u0nBjRDux3Q6qZKfkqrij75P)dX?--{v< zp-lTX!7vZzIOz@OEE)QFl*qbBQib%?Vl{T3!5S|x9!X>qrYI9Xvf gp1@u)`ycH?!wUH`Sj8G~oi@IXO_D5%ODoa)0;}bCW&i*H diff --git a/_tests/out_app/com/lanremote/watch/MainActivity$31.class b/_tests/out_app/com/lanremote/watch/MainActivity$31.class deleted file mode 100644 index 3a0d2f2f1f2036d0536bb7c7d2bb02032203d2e9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 545 zcmah`+e!jK82-lH+;!b@wet}%12t&1x+{W^fW#RcP_|0T4CT`^+tlnl; z8%2~D%G2^Q9>{ZL@BT53kJOv^obSPQi;oC+!NvL zw?hjP#bDh8Nyx>yOtHnki`h%FQQ(>4eh^7D=!ke2^lfaUW1H!Slqq7cFO(8t%lD#4 zL@3k#O)$)ZIZk>5I!lIr9wo9al2jo*wOEbaXRyW#j7QQKmWXDFL%$=s4EqOPCQeqE hjVG{I%>GA*(6B=O3|6s5T&In%W0NF{;?hd=z5ulscy<5) diff --git a/_tests/out_app/com/lanremote/watch/MainActivity$33.class b/_tests/out_app/com/lanremote/watch/MainActivity$33.class deleted file mode 100644 index ccc4475bdc539177e72f1d9fd6a8db98e137c045..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 545 zcmah`+e!jK82-jJcU`w!?R-ScKn+^S?usC!Akcye@7HnAk#!dKp!HZmQFPG*^ia{i z6cpV!F#qxU&mW&}?*NXllSK-ejJiaZdy{ z-wrK^6@z*0#{m~-GQk%9E@m&uMuDe_dwwX@pd+H8-?y-ljBO+%Ql^l>I#)^rEzb=@ z5u!xf#A4QWpTX)c&>u--SR$Gsj`TgzWmrG>5^=J^ hs6T)0g8q`0IKy)VD9c#Hr5 diff --git a/_tests/out_app/com/lanremote/watch/MainActivity$35.class b/_tests/out_app/com/lanremote/watch/MainActivity$35.class deleted file mode 100644 index bc3871ab487dd87be60fe292268ee46782efd0c8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 770 zcma)4-D(p-6#gdJX1ANwr0ZX8{fX&TvniOO7B3=#6bnjP!APKZQ8tsInXs8HyR(&E zh~D}H-slT>BSOTYsQ12yrB5NAr9wj~)Pb3E=5WsUoxfjyzWxAk2Md$PA#cKPFo6QY z9FK;LFi^1=Mp`sp1e*67tASJxxR%>e?|8SD7z(;C6K|2B+B(UsYGIrr$A9VQ>#ic$z8|bVI=~{~rW? zhrw8mdgNYeNhQ|OVOPY@4gm6TN`121Q5tixA~VdX@rrI{tQ7cA@i0m#omHXxQO|*& z4baMlMMw#Q^GGQXFNZ;rhy)edBnu2z!OG=qn`psubc?t~)+%XL`ezoqzVI2W{srbI z`q9$STObMJCA}V;fBts~g{+vZkH99r!&=X~r`qd=TWarO%Du3Qi-!Pq25r2;jPV+I Y%u`qn*KvcSK@+W^PMS?{Spsza0*L6pr2qf` diff --git a/_tests/out_app/com/lanremote/watch/MainActivity$36.class b/_tests/out_app/com/lanremote/watch/MainActivity$36.class deleted file mode 100644 index 0a0167dfc961feb70b12413ec35b0bf04f4a8e4f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 545 zcmah`+e!jK82-jJcU`w!?R-ScKn+@{@TLet3IZ*t=zbjs9a(2#4@S53L_tw>(F62Q z(Z3WF-8eA+@%zsopKtE~_OX#g3Yw0zg(+khHn~4?JXZx` z-BtFQ44G&k!%Bmp+@6F#Fkv98!?cit#b6KJv71!rI~U!d;1OjzluAZN3|h5zV`2t* z9kzu6iVUSm`5AI9=OPR%yUhl5v{jwlQtd{^97FN9mCINux9bUpbj$A%%eSQx=kcg3 zg6nUG7Q~9dyz=9Ki&L3k3x5~0lVqd76U9A0l&aqm(ZKInSWCt>k`XCW$Y7l*C4!dc zhM@>iqWv3Sm<6-#6#X2EWSu9eOnPE5>)X#@^%v-mq%kZI%@9ZWp6DX1AAE^8 jSz*+lz+N%^AN>FgOXN>s87suowDDD}lVnm{Qi6XXDG0QnqWg6mbYz`{Js3Sey-!dSUGxAw zRP-+eMK=!2fBgRQ$LHHSfPHLak%FcpZD9%-hE48|9M4sO82OQKZrz9voQ^BiF^}X} zMt7CHCPO9~$gt92D7Po!4@?-y>M$+jU@_Q3ckCt=`p!jnD0oEK4yBUO5rbB(-I$m` zUWaX=fFeU_QhtV<%ee@{%5Jkk9c@)7w^X~)F~?B+ZRIjn%I$iBA>HzO#PV&a#CbgG zis1U&p#`yGFt7YL;Nny!*uvk%>?GMJ@I-OX52fmNL^SYw7S@unjbud16f#(6N{OK5 zxnU?mlxY737-qq2JGlX!B}G4nB3b82DwCdA%=-2-Sp5b1BWVl^L^H&Zz9+f}>jz&V kPF5K8C$Lvc|3^PS!xH&ZSjGzRG;Mqp>m->JmsFzn1M$+jU@_Q3ckCt=`p!jnD0oEK4yBUO5rbB(-I$m` zUWaX=fFeU_QhtV<%ee@{%5Jkk9c@)7w^X~)F~?B+ZRIjn%I$iBA>HzO#PV&a#CbgG zis1U&p#`yGFt7YL;Nny!*uvk%>?GMJ@I-OX52fmNL^SYw7S@unjbud16f#(6N{OK5 zxnU?mlxY737-qq2JGlX!B}G4nB3b82DwCdA%=-2-Sp5b1BWVl^L^H&Zz9+f}>jz&V kPF5K8C$Lvc|3^PS!xH&ZSjGzRG;Mqp>m->JmsFzn1=MYLwg3PC diff --git a/_tests/out_app/com/lanremote/watch/MainActivity$39.class b/_tests/out_app/com/lanremote/watch/MainActivity$39.class deleted file mode 100644 index cd76f1908bd27e7ec759bd9c15e1570dc4d380f8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 545 zcmah`+e!jK82-jJcU`w!?R-ScKn+@{Zlnl83IZ*t=zbjs9a(2#4@NIh&k__x7d=1^ z75z&=(TxN1AHV_S%p*CL z(OqS)$&iT#GORQh%I!(`0}}?aI!p^WSPb^i9lJ?|zH`wX3La6mL#bqR#GqAcHzsC~ z*I`>IpvX{~l%FBzaxTKKvfFG>M_bj&E!A#x%rO*yTe*yta=V^jNVohRv3y%9aUPGl zBDnr`XhEzP%qu?*xHy#ww(xf`J4rSQJW<^9L#g^55e@vFg|%dCBN>r0g$&l2QX*)1 zZWxLXCEC9MhFLJ%PHsSFNzu=tNY;6h%A_Y2v%dWdR)2y1NE*We(F}2D)S%p*jhh%Y-fGty#Lq%1 zLU8DjLw{7&F=|B*MOn%7W<77l&oln{>-q1N^L$yR!_A(Yiu)=>{w@wEn;_O(c7;6LxD1UhVoR#4f)lolwqlJET_^PH44Av z#>hde+CBs-9_9tA2pEDp%HiNxMV5|!uZ^}J7z)j`{u~yt=wV6Vk)to%(f?K?UYZ+* zo#t$%{}Z&MEZUM(~qQ@ diff --git a/_tests/out_app/com/lanremote/watch/MainActivity$40.class b/_tests/out_app/com/lanremote/watch/MainActivity$40.class deleted file mode 100644 index 47c5a55c1f0170b93ff0f25e11f636faa5ecca4b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 545 zcmah`+e!jK82-lH+;!b@wet}%12t%cyeoo`fGCMGw$J zMgLMzbmPGM$L~LXe7?N{Xkjad3^W6}jTvMaws|mieNTmA9K^!C^phHN~PQI&*BXBz&%f{B~~%SIkHgER6bURq(`Ui3zS$CT|*Dj6R!Xtnx{g*g-q zI5vtXF_fp}XUKb;izuq@H=ESaPHlQi^&0~V45i;zE)%7^o-Y{mcF-qQ=tw2blW|Xk z*WV5;OcaB46(k`Sr!vJB|1M@P%|?MIiu*w%)u1clVbHg+k&bPqBT}Y_!9G(;gl*r8 zA`ziX`!~Tb59T=O4d^Tx`gxSdx=2!m^weTCcAvo-FEAcSV^|`ZB@X?b=rZgde3>{| iVK$z?UNQS0{QwOs<$_SAHD0;#4Ns!r#U0CD|zOM7f?HN;T+;Xz2GXY$RhF$%vFGWU$VZ5<%PJ zVJJeBX#WNn=D}<`xdELeML&lkSr<$_SAHD0;#4Ns!r#U0CD|zOM7f?HN;T+;Xz2GXY$RhF$%vFGWU$VZ5<%PJ zVJJeBX#WNn=D}<`xdELeML&lkSrLwM2&SifrctqKbq>|AwgI2BeOe`R; z!?sXBk)bpzKSR#tT!dkz*=$fpd)3)3)p|OX7>d8GT*XSceNQl?+x~!9z9W^mh$np! z+oQ4Y(i4kW-+u2*zMZ%T4A@mHvy z{UW}6QV>D02>Jv3A7lSQJaHdZ#$X0=&bjxV^PK0r{QmRf7l0<3v&bN;LNPFd9K*aH zj_N~CM0^xVUVr9Ezh7VX0&&}y!B!xjTg@gzPWFS?YA{sVhxxa3XqZ)@8_2_8C=9$U zFU_!7f6yIpUlQ8QKm_s@L)NZ#G#n+vAN4d881hfKCzFV8M2XN~GAP0uaSg{A^l;3@ z#yE^*tl=b~jJ?Pk#X3qjt>TP<$=;cD4PFQX+#PRXQfZC0t0u zFEUv6lik-T!ma%Q8ZI#yJFj1Dzx=fG@$>eJcRJ=_saP;@8CMv}2a%X=Di!MaK96H- zB0k}vB(K3B znfgB_6uQN7?JHsCenNAz<`KtL%sI!MF^i6yGmkm0YMyXhZGuzI1n1521eHBl6AHn; hg{8cQO}e3cKo&~`nn4{6(hALc8Q00uDOpO7zQ3Nb)d2ti diff --git a/_tests/out_app/com/lanremote/watch/MainActivity$45.class b/_tests/out_app/com/lanremote/watch/MainActivity$45.class deleted file mode 100644 index fe3d366b662203dca8489d5bb9bf4d07b561ba18..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 721 zcma)4U279T6g@Xtv*~7Q)A&)pjcuw)1h-&(iV6}GgjAs|d7tbK$&_RkcV`=Y_RW9b zlP^j^A4E|riv9rqN3s41@eT^{MdHAG+&kx=aw=fd+*f=?)KiydJV z2EqOQwhRrkT~}I}n}mG5(erQ^3ogn&7EvKoXYmtCA`E4k)~>eJIHMc&*(o);LPtrg zqF|z;fi%IEd}w+~PTEBrB~%Vxzu15B?BMa2{k^x}cR&Al`rN~DEV(%0<0MWImi`e> za8qe48{u@lJ1w;{6v?K1kVzfNcBALw49iTvH!`h7e|E^e74!o_yQJgA0SeIrPM?O|m?y6f-r9pF}~bT~tuDQS)#DCkgY1$=y$5#mVttOsKZ} z-mXHXWyFO)?K(J3D1U$X>gRO2GkdW!oBnw7+QmH1+BoOoJT4IGhtVE%O(+FnDAKff zV|AH*KW-gue6NmOr<*#FYODKKW&Z;FzKu(S+Ws4DWJ(2tNDvnPL(=aPthIQ^@N!Qo z@gSQFMDl2lAfIIXOzvi!C84-04QKVB$LmIw6Yr=nic_w4UFcCf^l;7e%`rV6R7k*-vr}o%J4b)(7OVz|b6;SmfQ}zFtL(PcBm!QT~1d DuUWh6 diff --git a/_tests/out_app/com/lanremote/watch/MainActivity$47.class b/_tests/out_app/com/lanremote/watch/MainActivity$47.class deleted file mode 100644 index 82d2a0eaba32a85a6268d6ac70412573785bda06..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 745 zcma)4+iDY06kR(RGt?l+=Qi;G0Y)hvtOI8JTmY;#)!d z3c=^T3QB_yet`echyH}PPb;)&p&mH5&Dv}2+s|L$e*joPID-O;E}Q^oP$DeE>7lEg<9H5c^&=Ww1dcaqw3ohtTC2V+9574{AU zaw8Lt`)$|5MMALm>GS^f_TI;x{SP}n=5X1?l>k?9jnFuW^LWODaumlx>*l@t%bfG; z*6B$f262$(hRLMb?EV$jzksms;s&99RG*i*Qqdq0gvI}m4EqFUH61d%(vwQO$R`7l zy*eZ)W;xf$-$?T;7Eh(+tR2tsu2p5nCn`=-&GD`YGfIa6?%1Arwm;0IxsuP664`1J zX)QDwJUku<*Fn{)9Uz{m0`D?zvbDjldH!3A-(LC#YVX4R!au?-K1+PVVKbwGKlmj$ w){08|E2w^Uic0;$6qk;id`su-V%C|Why{ig(8MC&4)^soTKw{v!iw_x4HXW#C;$Ke diff --git a/_tests/out_app/com/lanremote/watch/MainActivity$48.class b/_tests/out_app/com/lanremote/watch/MainActivity$48.class deleted file mode 100644 index d93d56f26fb098b012d55d4d7da41ae94f26b2c8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 752 zcma)4U279T6g`uy+0ABcKK!cRF-_ZS3Z_C4AtIEbP)RLV3BJi@Gc*%6v$8W=@vRTO ziVwc2_!mS_EXDc*`a7)RpOD^Zg%&N;g`JPfx#yfa_iq2*{03kSejXWQZCD;oAjdEf z#-mmg$b^q#&0Eg`9S&Mcfspf|7HdL3ug~3L$mxMl^;w2m=Qw=Zg@e2e*FymwLunYS z1x8`DwcH!>P!rp2A%(ufkZt%~2V)HRRQBU1kx<%231u4<52tXNVf-k$`zn@{9QTF{ z<%ZwcQ%JRl$oSK?gR=~komU@r-oF2}zW(#giyyCFe&2fMVjSmfT<~xaml&!?F&{3R zp%8>2SE_#F<}8)|xN&^#`&RafUD1h?H z80c6IBO{xqff0k|ZlLsgVboZe&(+O0gsd5;q}n1>Ily4RGZNzK5x&Sr4!I`i}c*bXzEiyNJuE76; puOe-u@Ec!2rMc4lC5rbmzB6W(F)2L93$__nu*^)%!nu}>*Dq8#n+X5_ diff --git a/_tests/out_app/com/lanremote/watch/MainActivity$5.class b/_tests/out_app/com/lanremote/watch/MainActivity$5.class deleted file mode 100644 index 74946433bc099e2d89f36a095aa01fe8bf898a2c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 649 zcmaix-%1-n6vn^V#B8!|n?Ksts;x1skVJ5WwpSLR1Pemcf@Qs)%noLBGXuMmh>s;$ zT6)n3@S#f2prF)?EbKXRK7RYn*>mpx{{92tBRVyBDETOdSU`oLBg`buxX$IoSQ%e& zE5`8=SNe;vYNqUU^ogNj$Et{Si5||w_X1Q=^AUttM99#%;4|(nIFC<87gAVq`>eFG z`wXRSFA4AxbsvooO|%%=^ZXeCE`%(K=)*2GwAG#8Q7`ea%&^IInj4kIGbOL$ghF(p z2bmI=LseL*WzJ9@m{c;0~7H?|hVz;+uPv;dOWT%xJHdP+zBJnhW_=IllRH zggb5qvt t)&4EmFT8l91E+fleD4e;yd}oND%L2MX{GCUPp1}X?rZlfVFR0$+CPDpm_Psk diff --git a/_tests/out_app/com/lanremote/watch/MainActivity$50.class b/_tests/out_app/com/lanremote/watch/MainActivity$50.class deleted file mode 100644 index 36f860db8a6a76ccb81e155e2b70ae8c3ed897ac..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 648 zcma)3+e#xr5IvQ|%s3fM%z9hbOVkJ^f)UgwMG#p*h`X>#-X~*YS|&ZqbVu=D6x>f( zP!u10&^Ld?zYwc&L2(~qLw8;JRGp&EJksS zGv9sE`rBxGCXwoJ?f*=r;b4?de1rb#C>1m#L74jlN2Ni?Y{dr*&)20AKRWHENcLYF zq?3*!czbat3B|UwJqO9thGU!YstZ@3>NT9d91*_pn`Mj4J-_4d|KZC>+bsO* iC8*d}%->+(W5okwCK!{#B&OJASiurAF$>pPHa;(i=bN1X diff --git a/_tests/out_app/com/lanremote/watch/MainActivity$51.class b/_tests/out_app/com/lanremote/watch/MainActivity$51.class deleted file mode 100644 index d5d409c6a7f430335a2b5677f0db2b03c067e6bc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 651 zcma)3-AY?Q5dJ2KIVUH_nxyvE+NPRZ{{0je%?&K1K7rT4k@G^WPB_jOIQ!1aeWl1 zSd1eh>XX2PgZh3T)yL4tQ)$jB&9{WC8Ax4e5Y{^L_$?1Eat=HndH96FFgOjY!b$zG zHx!}aYVW0z<^v(UQSG{TKydXSG5u(wTom~K*ZI}&i>Zea9y(a|@d%Fz<$214d=Q30 z>&m-kgL`dn%x|_c>+ZCO3CXs{ZBOhS`1;Rp-4&#B) znH;88#)rZt{dh|#{XVhkK-n+#wPixV_LZqG5$4^p+jXm$cTB>0&$R%A9nnZIw2j<3Id3sJ?Ck4F)vF+-@06DAZzl1O9f zD{D*K>SkknukE|$c5SBg-uk~tv*Y6lp?XLCG}2mh2a>S(9~#XLA-|FKm|kitEw@L* zuFO9FDd1%zO$c|>QI^P$%GIp=J^eRMl@mYcWRMzea7)^L+KVvfW*fLUawS8EHno=7 z#y}V&4QjmP0EEY&a@j4Kcc0^1#8XC}@oS!AhlH)yXQ0+O{1c7{&)F8(BY(;E1)^Ji o9iBrGwN63R2i4MdRPJY7F=v)JIlROx_Ici5jg?r1bDbKW-(k(1asU7T diff --git a/_tests/out_app/com/lanremote/watch/MainActivity$53.class b/_tests/out_app/com/lanremote/watch/MainActivity$53.class deleted file mode 100644 index 65585f97a35d23e7b2c7b6693d957bc133849e55..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 545 zcmah`+e!jK82-jJcU`w!?R-ScKn+^K>ZS-n3IZ*t=zbjs9a(2#4@Qp_6h#+3K=n+~ zzZ4YRI57Y5`_FHmFRuW0v5`dznvS%EDP$Nnxj%9|R|R6^N5Z*vBR+6Cu2jc7l4BX& zRrZ<;nP?!xN`s-?o`gRzVIZr+w2*_vU=Q7~n^fpK7u})Y5oJ4+N=8QvTD5j#Vg`8~ zwuJ(U45dl=8FDV?A`C0d{RVZkRh`^Y?MBBOL-Dtj%UCJ5>j{Q*%kL4(x1|#2@u(|; z>u-k^#EQYZ^5cMuQ<-23e;2cpWTU_n#XUcis^1aO!0%aDOU5>m5h+v1V4W!?f|loo zp$Ji;{TpDI1+(qs26UDb{Tzy9ohPYGdSWr_+mB%NC+PR2F)R?x5J&o*=pw8ie2F+& iVbmYMo-zF&{Q(V2$d80`>qdOwbX=*9c_ha& zx~uFp88Xp8hLr|GxjhMgV8TFFhiM@Pi@_ecV>hYLcP_d^!6V9cD3y$k7_@5b#>5Qr zI&2FC6d6jB@-yUI&P5njcJ~|9(N=YGOSKyva}34bRxV?u+^#1W(k;J7EZ>$&oX4ZC z2(G^!S`aG+^U99{E>2~FE&N@~PLhoRPZan3P^x}ML<7HPVJ#WkNJgYgA%k_Mln7d$ z8-^l8iS}=RVHV7`lN-=kQuK2ul69V>GUlg6+>G(#NeccP21e()vY jWQ9?G0DH#tfAj}5ERjEjWvmcS)5ce^PLfG+NhNw;@D6!8 diff --git a/_tests/out_app/com/lanremote/watch/MainActivity$55.class b/_tests/out_app/com/lanremote/watch/MainActivity$55.class deleted file mode 100644 index e0ab782532741c6fa805633cb8b6a833489fd649..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 881 zcma)4-D(q25dKcG&2BfVP1e8oU(>B_Qf<=OTtx&a7L-)M)j;qfY&OSaWA<#>pNenb zT@k$T8N4be1|dFx*CP4=wmyY8YlVhVs0({$&YYQVzWMp``_pFtD_EIE3TYXVhACti z<{aOv3~do`&yRTJg&jHl%DU}}J5JGW04BI|J@Sgm%jnZgiU=ZI+q}j+5eeT?ILY>@#jhalqR_EEMPrlCV9l;3Px7 zM_Rix^quEzBH{VuaGJn2?7;Ry6$PA?aZbZ|Two|1M)Q8?3(Dnp283po8h@F^kvpV< zH;8uumy+=VCE+)T1>K1;9cPi{gCLa^QiB4H7q9^ zp(N8Mq>w?oCj<{xhjtk95VN$J3K&eVTrOEp+SnBRS(M4TKw6RRiA60hd;lwdguG2R zhU;`@NJ9Eb=M8B8{GSjCS+P`pPnel8lvY|lX0;@J#%fLJd8?JtPgpHkKV`L)5zbg6 tT-0YrDDK1RkO=l04Cy;el2z#k(pV(W6e_5alxW^dxJjBy$r5_>{Q=b(-8=vQ diff --git a/_tests/out_app/com/lanremote/watch/MainActivity$56.class b/_tests/out_app/com/lanremote/watch/MainActivity$56.class deleted file mode 100644 index dfcbd81d91a1dfcfd44d45554a55f26099f52d65..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 868 zcma)4ZEMp|6n<`-HCYy28{g`C?J`X}UAwvYr6S01;OG>L7J?r}nqJ$~`4 zAN(x-20^hX`UCt6BKoQR3-QDWD`PMNx#ygF&w0*sUcUbR_yxcUR;H0dUWTM$3I&Ea zHw+rSBO)Gz32!`e5_izpbUbm}O}rg1d2X!UU??O5FE*MC)%IcjEfor;WvCivpfMDO z&W@91=r`_nhulpF?WQNZq$5#NeZp+IMlgcEQD#~IY8 zp6_q<$&3}8B$RC@a)MYz8K-5O(Qp>$7|I9n+lWI!Ny6@sp=8$De<`Jj=ToKIM7WF# zS@=Z;!+gB^IzhO#KS04H2JP3USL4@j#-q{r#Y+`)Fl5YYxQr_dm4ir3HkAr>T$jhO zvAo)(wyv9p_jMoZ37S?Cd7|H{b!5~S^u2~ZNQH2^K4+N!j|8<2wRk^5}h6Z~Ep zbb0h}7a&gqp&3|V8o7Mk%P>om6)k32De#VP{V*mvn>-nWJq^p*5-8cg2`Q$&-4%jI zYrYf5JVu4KQ2~PqRxD;4NlTiee+G5BT_CMWc6OuI7v6)_KR|v<7QJD_e@B>w&rs~Ve#EjReb%z4^pa&4^kbGS>nAK*8R3*Q!g;+iLUj*TheWX7 hVMsq1?+>m5)))W) diff --git a/_tests/out_app/com/lanremote/watch/MainActivity$57.class b/_tests/out_app/com/lanremote/watch/MainActivity$57.class deleted file mode 100644 index 64740553dc3f82b4c0294f340dc6bbb5b5f8122f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 875 zcma)4-D}fO6#v~eYqBi5Hh$Ib+I8A?x^{EJhl(J>fumC}S_nSKZF*f(lADpF6CXtc zUj$!$6(9C0_)!G?1N<|@PWDfTCr(%ygBi#@=iYP9@BGfk_upT>0a(DoI8sQfPz;PA z!!YFpe$C@D6n+qk+7li--P$U5hQcDLOVPE2UmUFpU*7}DiROT%GO9KWL>%P{d+@VFm}M`2%TFc}odeWBqf znL-i80hzexX*fpbLsq$7`$Doz9t!HnC?_LdGY;Hf_+`ly{T~S`E$aGG z&>{E9rYpsrzTXz%{T+Zb&4od?f_~_TWjDdhjaD?9WTn7c((!_b=&Xvk8*~iJC%d2} zb0?&T!MH7@2$wt_MIu6hmQn*l87!Mk7L#^0MgIh<^g2UYk?zThUY+>}R{ae1J>3{C z)0rU&Ss0O{ZiDJ^KCtQB2yG diff --git a/_tests/out_app/com/lanremote/watch/MainActivity$58.class b/_tests/out_app/com/lanremote/watch/MainActivity$58.class deleted file mode 100644 index 6ea4ca545e5838e7420f74d258515d996f3ec566..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 873 zcma)4U279T6g|^yv)j#TlJzTo$28R>)h4Yi2oXVw1xu>nYGCo9Y&K)FA(<^ZvlZX; zO?>jzpCM=sg8l&ihe{Fs3F2KVG?YSJ*qJ+b?mhS1Gxy8ykDmc7VsQ!+Na@flOd`!N z?T5qqz!MQ4hKkppddlzB*StX7_EoSIsAuJ+8w_dH3uL*$P--0(-!x%hN{4A71B)To z_qM#G!bbgmr_X&wXg329s9OxFO0{j^C>j2+YaqvvdBQyvM|?esg#nvE6W)*;IL?rb zxKvT#t97X;MjALtP@7)l4W)?!PU|>h;VjNE6b>S}Dnmh)!cLzdU#YhK@`_b3pmsNj zbpaQW^otDT%H!SJG1krf2?j1PSUYcCe|@?A{nhrj=Pyi5qpV}b!ev}xC>}&&Tv_VY z^L;L5d10wR+OAX%7k3})F`5o(BQ~1VwvH-;y{GzvSO~8(;0!bWQJ~r;&6mS2g=brV z;P>KTher>00aElT45kytkor*a;vW$Tc`;vmN0{kPFx-@V#Bnt{>$sD4-f`3RF~`;I6OL<)aLO6syj>ijvYESB4NnT!mub z2oekvw%01VmhgGY3we3P3hicj)^fysJ9L(v@R@n*4nrbrI)ORGFkX$yujtS)q(V24 zgu#$rvX(80uvmUlU*dL1-R?TV3GXq)^M#s*V+^U@`o_-I^WC@ad)uqM^>-T5l-7H( z+WYuTLxv%@*InyxZtiqm?QV4Yuix}PboRP0RUBuSjGBlTnAdb^F$}ZNZ?=du`w54;|Ta z_<#Lem8ewsK-jJq5WiU-Hob;{yi|uKl|Y>W2IG+s+@E%>AmD+D5<}?_Bx!^JV~ml= zH87k5OQ)sKN&7MSlN4dNK(}#vOOIYGeE}?a%(>g{0+kq+(vskEAN7|w#j1! z8WMWZ2gpN}&eB3kePMxSb`GD;j%MzE{rCys5ZwkmRD4uJJVK43E6gm;xh~|)SQ#(5 z6_fakEB#4WwNUmdI%25Vi7KN5qSJNwaez7+K7tS%2pO7FzTobH%Xm1RN@27yBC~m#DZya>25fK(|C%cLD~dJB@HoZic=4ByC_BDB s_zrf5#}9PibT5PNNmTHX7!R-TnsSv^`UY?5)F#b+?Vc^X!)~qd8;|jrYybcN diff --git a/_tests/out_app/com/lanremote/watch/MainActivity$60.class b/_tests/out_app/com/lanremote/watch/MainActivity$60.class deleted file mode 100644 index 8681f7ea19cfa9acbc84f8769ac152ee10933832..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 867 zcma)4-D(p-6#gc=$!<2QP1e8oAJbHmRGYL`Zz6&e3recsYGCoAOg3Y)A(<_^lZp=@ zh%exs;GMUEVi4>D_#OeDqIlK{4W&>QcFvhI=X~coKfm^V>;hQDY8EMI26P)~WEkfC zaMT!b8HrJ-MB_DAe!sE71Np#L!A_vwRPHtzGO8cM6;jIW_@6`|b=WT5UdXw_QB#7R>8QO`u4A@@pfHIBq)G?peD23_)zFmak8tHNHm zISgWD;w*t|^N5dP3q_naaKXk!Tw*95MelJOO3D#-2MmR3t^JqLSOr5Wbes4VaU}`A z%227k+<%?n+d3Rz;u?cJ-F-WK|8e^6!|zX@EzF~0V8O<9++ZjjMPjz6R44a+5yzF4 zJ5B29diD6W9%4O1(^XL*w_3H1ff|Ez(C=qsDS3A&7#98`L9IjGUJHBVKGzPUcrqS! zMf7|hK%;puSZ+9ue6b!Rn1$JjmXfR#_)z-8FeW-1LiNL*jg@2v%w*n#6jR?GNhzYW zA&+AbqeRPSf}skQ&nF8>JDQ?DhdNmoNh{Mmu~_xRZ(#NBFuu@@;U=9KlF;|)yaoH8 z{|iDPE0*hD2{ZE(rl&b4Tu*oATrceuTrcCCay`R2<9g-<=iCV{JEaNA2e1xw5DF^k f6sjbfX$=~d2sDKTnk03a_cCsiW>K<)9({iRsyEU; diff --git a/_tests/out_app/com/lanremote/watch/MainActivity$61.class b/_tests/out_app/com/lanremote/watch/MainActivity$61.class deleted file mode 100644 index a7df84c413b0dc46a173a754221fb79241d671b4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 859 zcma)4>uM8G5dKbXyV-1Qvfko7O}Dy9wMlCAPehPn!ICPtHL&+ZG24s+N$|m7%uul!y}x9V*qcy|yF|5$t7-T(2Ge?e@OOr4 z_h^8M1%~3!H~YWdefaVIt%gOI3YK(S#|?(saSUdAN%e8p6;Wia-f2@eH>{J}d4%!| zM>`HZxzla*6*L%(!#+Pvq~wE%U|9N(1dTrRc0Cx9`+U!n;&I{+M7Vtbkfk{=Xm*f< zuGsKW%*t#LtV6ec- z<#Z8gH#78=&?M_JX?1$17OlDb8LYVvVUcX-=a9t;fo9M` Sn`DmW-N9|rG)k7zqw@!W0L@eY diff --git a/_tests/out_app/com/lanremote/watch/MainActivity$62.class b/_tests/out_app/com/lanremote/watch/MainActivity$62.class deleted file mode 100644 index 25edacb87110aca1ccd98b57a21493feab0d843a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 859 zcma)4ZEn**5Ph33yN)4=TOfRg)CDJnCJlu@K}e{g5-0@;LnMnI$Z?j&k-e_$by2Ut z0k{DNKmrjVl>=}OI0j-&ks4K%iY4#N?#{gTc4mJ3{`MWf1~wLuLta5aM-e54WiJXl zfh%GjMv`~lxzd|-wq0L5@}$4#%YE~HkD(+deqweRnuF8g`x;a%DA072p)*vc?w*@5 z>~x-wrreW+_P`gue8`ZuT0<3Q$??LmiYi0-Eq7%a^EYuSR2U2e;f7qr5`z*%Qu26T z#d(6*bz?V7G}LiX!6hA+afP9NlDMZyB#0pzO&MxdYw%Z3D*b?-xl3y6xR#|~XE3eT zhqrTT`^OVh+#vd2?+<={{GeeOrh*k6H*t%haT5J}E$Mx(=kX*ld-u9j%`NNnavsw= zC(xF$FLwH^p@J5Jaa89QsSxfc;0!DOLC_jfl{cd?g;xf?;Lp-<#N(HT0D0;IgJwr* z?C~u>!>rA}Xf0!<#K*!5qJ-pZb2*8|I(k{})vV8ilrZQ|gy8XJ;3f%A(4Zky!C-+^ ztJxsZZ06`MqfMu)?c?6K3fE zs*^X)*iONy*iO-?*-pt=v>nAbXFKW)7wj3X8jTs6N3aG81Y3hyT!cloTUi0VClBW?_CXc>9UDnLA diff --git a/_tests/out_app/com/lanremote/watch/MainActivity$63.class b/_tests/out_app/com/lanremote/watch/MainActivity$63.class deleted file mode 100644 index d53643d62d70eeba27ea60c54a511a5e5ae81b1c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 864 zcma)4TWb?h5dKbc+0ACP$*MKBUShh{O{z_L(U*uI#eyYOa5b>_P);_d&4%pRu$PLD zzWNt@^4%9fF$nqt{2%JS5NEB>PzrTn&&-)K^UXJx@4vr%1F(UO0%nj=kkyexo?+1q z{N{+uQ20SCnlE|m4w^gMlMme3JMiLH=G}FMd_3?Xv&B&BoaW!wprW8a(@})ZP#W?B zo@Ur@KIsjG8xz_+PkQlvhKyD3syIuE>-SZZ7>av>$4Mx5!$hhu7_yT4LdAK8f*W{p zcW>m4Rm>5{n1|etG*oa&!Mu(ITxO`8L~lC^B;^QtLx!?d@BC$y#NLPs9TVRQuBPGF z7)WQu5wNFf9E?f_j%)y&3e$eYWFC@i_5&B7AlT zkfC8PXm*f&!C--vO6fMzg3i!iM1!o$q}Ax2TC~RUC$Pq6C?Dv?aEs17N#y3}ybb-I|8qhi zD^?pH2{Zo{s*^F!*iP1%wVj+%ww=6j&UO^zg6*giT(l>+VpJ!n9l;vN5^M!#ZXOoN cR&D_qtPto7nrM;C(!5u(PMSu^QhN0L0k#&;i~s-t diff --git a/_tests/out_app/com/lanremote/watch/MainActivity$64.class b/_tests/out_app/com/lanremote/watch/MainActivity$64.class deleted file mode 100644 index 55c3668b9f4d6a31fcbdd83ecb0c9c77a278c0e7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 706 zcmah{+iuf95IvKdYiA*Dfp9C(i)oW^aV07wRD^^gkRqk+Ls8y0_A1%ptR;Js!e=41 z0`b5H@KK0aKmyT+Jb31Kyl2kw%zppz^&5bF?E0vn>Y*l3M}x4PMrZ>{OT09${;^?EHZfSP}3M5d4MAB5hLjfk1-*0#`g-6=)$O zgr{;Lo%SR?9-pejvd&9ww0%XWcDkbgYq;j&y1)&z2^-6l2~C+Ksw|^tFZS5$PG`B< zL3iZg7GYDGw8(WDFSI&~M;xMK)6aDBeyB^Uj4BAVex52qYp9Jnn$O0ncqhjhSNsz_ zG1ATp#$NuLMre12mzo}QM}*)cpBIUGs~vas-&sF(6JdonCdu+ro5`WF(>xW}a`Reo zvth}SAO?ml^)p$Ps`T)X`&`oEKL7m!Hh47xgqx^@q1zC^nePE#jS4?4v^n2qw9C2b zD!oS+px*igyxwOtKXHk0hhu{?>RsGrTtUch>$u0!kvm**?L_aKx##e{VD&FEaNp{W g(X2mViTl6!57-(|%FZOadvo{RbM8HxU%$Wo1ki`MBZ#3+N8E&ldWH*bux5FV zAM&+8a%*^N#fFeCQv>}B^>Rf-sUC*TTrGdwKmtc}7$zEEGBm9^8;;7b zY|SmLa#vEcVc`oo%21ciEEs6SF&#}NnvrDas1=`~!Es$4MXA1?9zryct_>-3pv!!T z@|Y*MFYN?c2#>o~NZ=%c`HVZV81ko{h@^p5oYv81;tb9*v>t?TItqL`A1tjhq|#4v z)uiprj;SbxN9A3&HR=Wp=c9oRoY&E5;sR3t;a`cAA?`Y!$8bJf6>_>dM*>|8i87T) z6*_GqgKR|!dsPXqS4XOVpO>NVm-oTXQBA1Jjks*$3a&C3<%SHZV1tM1hvOH*KoqPX zvhLH`}LV;wfb@lvlvU`@@9zi|1w6oIc}zf`|k(!{tg)FpCx1fKb9m6=i>uH9@={*6n?Ejvk)X}$m_A}Tvj%q)USgcDP&o9Q4C-U1k z)q-I2Y-M+O1zssRD8+6&bf2Xo_h~-=l7qVzW@vnHc>&uV$E}eZSv=)<0GSm)CO3;p#mhX!BMB0xRM02F3TDZaz zTeXE|@JFVwiF~a}-TJ>*`=qQ)MPBJ7Gr7{!L+NHF<#>o~25zNJrF~;fahx+e?Z!f< z)~GZ(QR!5=sO&9lEKW_~hMz9XFE4Q9EEOB}##H33*3uqiBF|-x4vmI`;RY%}P)-OS zq<271qe5RBTZFeLdPG<)GyUCju>Rw7c>V8aeWMh^ZQ2`zsDH*CiYo}{ba%Oz;x1)~ r1@E7c_6*(+tY0d9Vp0EsR{blrxVO~b#{G@H|OSXZ+`!|`~_eOaT_(%12j0AXfdowJ5MrUoSIv&k|W{e zEZG;@e2`w3+8@W;Zx~vBrt^4{VRf|V{!aD4Esi!qhOiV_?1h^u&#*EQCUsV)NvYLQ zGA6SVg5!38I~-j^4AG$|MI~pNe3=|7Nvzg8ZM1*SP#>(1Lo8z@z+H}e=rQyciDBpn zDOH}wTicr?|LtJ0{u@-Tj{`hlSiV;GwJ=6ZGR4r?wJAY&q>cJq%qPly`4~RT{AYMh Q*r0Ltu|{4<{7Mk5zapfbOaK4? diff --git a/_tests/out_app/com/lanremote/watch/MainActivity$68.class b/_tests/out_app/com/lanremote/watch/MainActivity$68.class deleted file mode 100644 index a1d8fe7e75b984607a2dd67ca636080b1f07a47f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1473 zcma)6TT|0O6#f>PMoL7gf{LIhQQKlIDgs(>h!m>@Z&-QVCMzt{CQh;~S79KlhBqug@?U%EN&$r@ME_i|-kt&8dC)eq>vWueyJgd1%! z>`$djf4a$=IL5H2Z7x)Ot`Zo=hy@!bDDWrRvT+1aDI{-nddpzK^kl_@? z{1Qt5I#27-FeHx@_V)!^9@F|CHX|?14v5U9Ln)hDvyXIwHM;ycg+7m&x6X z?+W5klAc(o*D4}ddeZmSeNp?ZJ7cih;r@qTUZ0CH<)Yjq5XcH_m*o2uGYN9*S7i*#?(`Pi1%)G(=SA=1hp*2nt#z)*Fy#t5n zZ59Pu^=Z!$qaW~Sw1t5W7;fSCZ00rCTbyj+^g@<&^93#4T5(_4tCg2coZCQTTbSHX zN%Ux3QY#Uzg?yXBfTnDGLbvf5{l*t$jjxz8zG2b$j!w))#BQNTGDf4}Hs(nipdB5$ Se$Hb7_b6T0XolU7cl`u3O?}(| diff --git a/_tests/out_app/com/lanremote/watch/MainActivity$69.class b/_tests/out_app/com/lanremote/watch/MainActivity$69.class deleted file mode 100644 index df5e2712bb5e70320c4a42a4721294ab06eca6d5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2106 zcma)7-%}e^6#j0=X2Y@+n$i}k4wR-fKY#>E5lM?8g%%8;q!L5by4h@DVcEsq4Q>Ab zpE^_DoY7aE>2qh4A2RLutbc|&{&v(i`yigX39_Yh!cMY#_uRAJ{mysKIrp1?zj_3q zA8YjppiV?khJX;m8P!@z8H#CZE0(LJZYi$1n3_^_b3}FZHQl`v9e9}`vGQ5JtTSNk#)?hUO(@P2npnq-Kgsn(7kTMcvfhVTQU`d``jK@IaTF1Tj#~H( z4T`F2juY)qr-{*ctTv_ifiAON$fJg?nQkVG7UH9>l)^Z{Am7v!w`^-S4Bc@hw4zPK zDH+e z{Y)8xs$v)nr(^pdC-<)r#yN&?H57*n9g`79!Y5%bm+<_4Bp>~pYwPC16ZEsBNk&8$ z!!a*RFQyUOR_^c_2}#s9AceGuUKxEj&v0bVkGl{l-PLTxwQPnHF+c5zsy)+y3m6db zl8ix60!H=_c3l|6Ma7xcZWA6Q(meknBjX}2kwm6vZrR25r5`bWg`sf|k9H%B5e8ww zw#qadr3C%q!}agCKjs@hCSx2|Xa&b5vR7&=^-S$N4plk1J`*w~@j7XtTBb_{=P(?v z!u#p+z*3l!G0mOplv0UrH#H?>4`_g?3mk&`3zNGOs(yzW};PBUEyoEUisak4- zJWU1gdP_;@qt8A;r3TlhvS6C%tovlt_LFme_#! zfZiBZ=pLdcgzxBXLaz9;Ej%?#{LlKNPM ziqZ=oz=smg(1JxTvasjO`S|TOXU}zsPbr!=-w>+C_ot%A3=zDgbdY7KH=_yi}-wSDTO7sW2Kcn zVJNiP{QwJC^id5_L!F^9%by|OLdZOi4)&;_o!0D*+I=6(4BK2MnNdkRQF0phDMaVG zn<{bDQ@NE|W(>uyNhHH!PigseJQ~RC2Op%Q_~tLlu-fXqGCFGasju&5oC*0^IllJ) z2zT8K$n}#JsmYbTIFoj0k`N!QXX% z(LWlr+cFrIz^YZZUs}0G?>y?HuF$DTwNrx5#~ZNDE&N|p(O*$4QHEzJzQx)=Yu=fd zSn2th2abIgk2#9)8T{>o&(m8O7#jRRX=Oe#WLw8K9uDxtMVX_5Dno6T6o!Hb0+}Su z-uo_z@3(fV|6k6`0HTk52FiOamu{4Wl#_`6QQkrRxBUZ!@04PAPPan{r-T<2 zXW-M{%gt@cEy_?8ynRR9JGehl+A=+=!YQNRRA|J}hW`q$33Ifw8tN2z#J7Uz`vXDU Bl9vDg diff --git a/_tests/out_app/com/lanremote/watch/MainActivity$71.class b/_tests/out_app/com/lanremote/watch/MainActivity$71.class deleted file mode 100644 index 89f4e094539db5c577c4ab9b4686b59ad4afdc97..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 996 zcma)*-A)rh6vzM5mfh{LLR%H_6NCc&P!HBG8l)9nC*+ZnUF#rOce zgWh}Ltx+Q(kq7WS(6=z2wL}^vsTaF*X3m-4`JXd0-+mq(0$9ds8VMv#q#O*vV3_uF z$Mv}iL`O%$eaRzl$KBvk-SZ;3C!<&S)kOv)+L2*?fnlmPD86FDLfVAwU>FWVW|!~r zIK#HP+1eFeM6^3n$>=UavQTVT7$L*!v@K*9%#J3P4lLvt5?a|fhw~=J99+OSL-quA zUu$1*MO*f`-xZr%3}c0-#o9j+BNA9?RinLFv;K?z5e+9&Eb!C2S;z;+nB<%iM)du%rZ=#mZrb448z>>L>T6mRu-tW zYlXqR?dkd>qEBW$3Z&Yu6dNWA3@%sgK+Cr4X%z_-xep}w_4YbVf-UZe{HCh<(tBQ$ zA-P4sF!LYTiw%ZURksDhXiX~dxZ7!o;0bT}v|{QW@;7Qg`pSBYvg^7Vcw$Y)+q3Vk~~zNuS2S9Lqt2e?O-XsQ6p5;mJB)@y{kgUu&$_vs?WnvgeF!PCI$!~_Fo;q zB)tm@40B+aO#C+Jxg_WtMv+!aWKGdLUa?E%&tRo5Fh9_Yp-g{+Mi{ggH*oVfzDkrN z`Q}QWz|60(n#tUmdNY+9tv82q*?QAR?&EBI9~W~I`?!7rv*R>I3A09-0P}=Mz{LWM VDH5@WTV&;q#gIvomvP%j{{n{-0($@e diff --git a/_tests/out_app/com/lanremote/watch/MainActivity$72.class b/_tests/out_app/com/lanremote/watch/MainActivity$72.class deleted file mode 100644 index 0bec0afca9244e44e2187718589be111c23ca53c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 916 zcma)4U279T6g`uqyUAv2)A&_eKVsZ!l4`dUOO=Qq(LyA(50OamK_;7NGI6sTb|)=9 z`dfT22#TT5{s8}n)L$XqZ7YUQNFQeA-aB*8x%bTd`RCvWz+>1c#1Pk!Ffj#<;hy97 zY?ph1==n<6dt5mkdy`A=g`?!IRG+N%RR&FUWN580Vk|brndjT`o3sha@TKH z$q9D2Bdjg2;!5XZO@?$W0*1v4uooK)iHhG63^O(9i8q5@Qv`2$)1`>+pF*BMO`tDV zBPgTp4+2MQ$S8ewRGQ^Tej<70Ij$d4UzM>er+@)xqtUUQq#3(I&2P^%6{*^o$f7&%V zp{>zwVe#z!I-$hrZlUxIZ0Zon?Ra*&zMaU<)Q6bOULWGde*hzb)}CNqdrC?7DJ_Nv YDA1Xp$tq%rtn66rN9v6#k|yyJcDFMMV@9gjIS$id;mkF)@v34G75+l7=5HfZoMX^rON7=8!}#)9{$)jwG|>-(VPMB;TOHv= zl7|ej&8h(t=X4C3NFmLT-g0&vMc*u~*0;DTiRz*7gnYyh&*e9g$dclA8cAGW7z|sH zZ1}t0ODQ@$NMeXV@1h$R#$_E>OpM?vL*@jy>Pg<@0l88z5np}9FqEt2m;do78@P^9 z9b+bLV4Pw2v{avmzDFVX^(_V~x7mYooItq?97)lpI&PPfuo%q4kMED(eLZ~p`RK!e zfm@i=ku#A;fnn@4n%)jj86DTOoQ!Mam#lcnDlP0x{0K)U98Yr=g^-qZ}b zWY~TbxO_<{$4qbj)2dSh_r!DCen`QuaM|)3CKlBQWwr^$?heMfnhPp-)$@2zX**%4 z=1%YCfH4|P6AVSf(rGoJ07-fW^fY3$Qn*cWnzTuhab+nK_rMBMd(aEtFz|^?3?S?_DcZv`9o>i_@% diff --git a/_tests/out_app/com/lanremote/watch/MainActivity$74.class b/_tests/out_app/com/lanremote/watch/MainActivity$74.class deleted file mode 100644 index d85f9dcb3d3b775627e782c67c91b88d64bdc642..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 952 zcma)5T~8B16g|_H-DSH#siOFeWx;+3tqA&1V`2zI3`oqDn(BkuZYOl>cDC7_VtC`# zN8_7`Kf=ThV#FWd|3LqR@opipk|y}DckbLhbMKjR@BH}v^*ew?+!;m;aTN&zLr@r| z9lu@lY!UFbFM08mEuCg@!*<03N4h(%+%@kmGAOd?hGvN&vwj-BtV6@F3f({w21BZ4 z@7Pg=?c!#=#T`l6?zzI1_Zi~ZTvf*?##E#Xj3fOI+>(JSwi#46TxoaYZV&%V!&!2i zc0qq@uC}e8YU@Bj*g4Cq~fxHE4a!qIf(D0&=-XE>n#Q|`+WT*#d7W_)RnGB zVsy$HrWlN$uiqYi+xvC!=J5TVjtr(%mlBS%is>m|TomSjj6P>PnXp4r&1BRLZsGX}aB$oXKXBb;|1%KRa*LhH}>mEf^ zU-U9EOk@YKVi>Wc?Yt!Ej!)>8-whnT>PE-M`-Pd0#HS)F!twl&8ot40({C8K9ZkVWea)bDdy=0@@}(j78b GQuIIjtnMTL diff --git a/_tests/out_app/com/lanremote/watch/MainActivity$75.class b/_tests/out_app/com/lanremote/watch/MainActivity$75.class deleted file mode 100644 index b21ad7297d14f56fb55508075fa05b0c262c45c3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1103 zcma)5ZBG+H5Pr5TcjY*2sVFF-2&Y`jOKTMoix_Am!GJG0L(=fUT(29t^?FzDt{A`h z)zAJC6M2bmKfwQ?Kf^d{Nob@AUUECLvorI|&OH0$_vh~b?%-iRVu-6q=;(vOFy;8o zylZ;_Z~76>zp*2yp5L;Cx9UVs*BJlFidWqvB?VYFAFk3-)hZ@=1LFwE|PI(VgQr^a?9TW$Vpk9y4Tvmt?@v^I0lorPF4PT|KaD?chWGW!@vzPQAymZG0d5#?|lr@)r#Yk z)RA#a>oAd}?nQnFkGf}`4k6ze%&M5vF^@dMM6VlnkCI`)b{rmtMscA)!(2Ddp5{OO z=y8>86}K47v%rmV-S_hq^m5FDFas9(j)Ihs4M&9@YJtj%7(ly+v7`OOma& zTD0BpJRX!?I}AAyT0OoHCW!Dl7z&7`(~|c9NqSxM;9~TrP$YSqv}uxYY01s(gXL!T zq2>-T@R3Z6-Xh($ diff --git a/_tests/out_app/com/lanremote/watch/MainActivity$76.class b/_tests/out_app/com/lanremote/watch/MainActivity$76.class deleted file mode 100644 index 15db5ef0fb755046101ec3da4175bd43e67b34b6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1113 zcma)5ZBNrs6n^eD)=`R#iGm`EP^B9$n^Qm>Vq}p718P!3vhaa)-IW!)-lgp@e$&L5 z_}O1#A}{gn2lzkqXBf|=BrLK7o3!Wj+;h)!&U4QB`RB_I0C(`98!^NcBs6p($uMq< zM&7kNpEpFv^RKMXuIJY+$9rgp&aM-_*5~IKl40Em^a8`!+FAZ336Rpz4V9s1*K+k2 z!mV*XU>MlkT-&jBEs3|CH$&g?wo58B^eQ-~p$}<>^ohgM>JGO<0$+4ICtPBP8`+H% zGNjmzS_%UcUo^S56#=Ahk$fXv#SktjxU69qR~UvmCH*)M9)%Ls9R}TK&nyBiWus(U zq2m%G&60wv41*{6MFlLk99f=<5%i{Tol^My;p4AwZ=_*dgN_?yvYM1n(08ddH;x2i5#x=m-> zv%;27dQy1L9Q!4C8>{k?YKoR`^A$%r_O~-KBRfms9(lGa0vgSA9@a%o!=ju^wMkJ{ zjwYEwTlGBdmt89eIL)Ff)9?1la TRtfh>%aD%*mQR7KOG))FgVY!a diff --git a/_tests/out_app/com/lanremote/watch/MainActivity$77.class b/_tests/out_app/com/lanremote/watch/MainActivity$77.class deleted file mode 100644 index 379e5b1d811dda15e0941bec39f12fe468549429..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1504 zcma)6+fEZv6kP|(Oc_SOqJp=GSg zDGw>SWys|`BG$j!l1d zX6G5gk?YA)##(fJE9Y~Iw9RT&)CzhgniMqQkc?&(hY|h<`C^z10YkSehC`8(lw+ke zf@o!^vh|GV35g>rqKFlVcS4BQU7Dr{d&+mYeRC7`Bnd)F9VHl9p3dhzrXmi?q!gOc zFVgn^S5I_1?K(Uicf9xr%`MZ_WOOmKY^8CDCBvNxI?*FM*2^$bz8=|FE0QeFq;xIJ z*oS@@Csdq-#t`}&qM+`0Wa5T`ba`iA&e^s;YnfW~nv7En^?3_$Cq6ovxNVqOpF4IC zXBgDA`zzlcJX`zv{Ku=iqA!Cg&S7YyFTGttz+`zs1vdHeg;uTe*<4kNPRmFTl?_w= z0ZU5%N15m}Q66&ACPQtK+vY?rGi$n61Y7dFn9<(5#u(Vn>O!eE^HCKwhF1nSv3-OD(B^(D3^ z#fQ*oiaEO^icwb+7$88Rd@HJ{{6hq!M>r@wMyvD$?b1`6lingBy~eQg22;{Ij8QDz j&p(lnF`%Fl7ipscNsQANAh%3lie{k#Q<~Bt$UnaUk{geK diff --git a/_tests/out_app/com/lanremote/watch/MainActivity$78.class b/_tests/out_app/com/lanremote/watch/MainActivity$78.class deleted file mode 100644 index 50fc850feb273104155f453c5e98fa885eafdc13..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1102 zcma)5-%ry}6#niu+O1R>QxO#fp+Gm3B7&$z6N8dWhdk7fEIyd7yNnB6OWHe(e}j)c z8sn?4CK8Fi@Yz2^{VR;8U}O=J*`z(E=brnW?|k>3Z@)f#0WgDGIi!$QkjX~RTeM7oY zgG&Zh)}Z5j{iz`_a$nl!>EKV}yHlxsc zvLSpwi$02Pin@k=oKbOB$2klz^d8`q1IZgaVo-@ei!ZM-^qJ*ikNkZxD92WxBUmWC52DoZQ5BF4Em3cFTTHe{d4!_k2lXXjA2}b zsiTMz!_ZM>+GS_xc0G^Bu`xY6MZzzdot59`^_N5&O-Du|XxPOS6_*$+H>gLUs9Ro0 zG6mAQD_lQpB$`F5uE&k#V8IvOMn%N5oTxbVAJxmn6^6`0Smz8q6%p`?%L*gg_7u-b%nndH^ia2f%K+zy-5X;A5(k87#kIr;ZAFZVP$_s2go9{?Q3tr+T1Z=%7%Iv5O{ zUNB+%t_=A^pt$|eRbJU1c7^Qql$aFiak9H7d4$1GWfAF4XMPPk9G#`nLJV<+#!1&t zN{Y_VlE3Rtx;kIUE~rq*k`sr84JI~PXhMP^vEVQ=ewTZSQlAl0sB;YUsq|P3E!bpY zvxP0#%FwX}Fg5U94DG9ecT8+!Xk9>2h1pjTevyZ9v|@vXu+zdW%`K+6DWQC>m-blL zi#E#V-oMXfks+H}PB^`6YA(3bh;}0R`~3?YD`}w<`$;_&R3R{IPA%U{`lXOIk+E

+d-^Y#L%xKwsd+eeC`#gWA^i^}r^5stVT)rvAWi{;Ce-LSwPRJioGlb#Q@ zLTb__i*5_2bjUb`945+ci8tajo!XC=uYNsw{&nV=_BvzXEU3Ji$?IAW395VES5YdE z4E~A;<^j~$RH+XUrSOjH@#Kii`NF%G7ZH)=;Tj=du+WP>hK8sdJY?8OM_!X{2~(=n ze!5W$m3pHhr8};5JH7g$(zSN}cdnE<7ZfQ*Q(j1ZwK6fz!_jKgSV5Get12O_)`t~R zCte6Dp~nY>PTIWq)i|tSQIG-Y`6Q_<4RciviWY|SCAWqoDU$QuDB_WcYjojOAjx3P z)sl$L!7_*5!pux#{cDx2%H@;)U_<@4)lQ3NVhwTdTWE<#p8^%@Ywg2l=hdxbOe_4#+Itg+EqZl({e*wm} BxwQZQ diff --git a/_tests/out_app/com/lanremote/watch/MainActivity$79.class b/_tests/out_app/com/lanremote/watch/MainActivity$79.class deleted file mode 100644 index c9bfca130db3f53d1854da7e4f6ce666ceff447f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1846 zcma)6TUQfT6#h;UWQ2 zpZeCzTOZoxn=aN8rPbvR=nv>auTONjdRhL0UiO(#1(G(-S~Gj+WS@Qc_TJw&e|>!q zKsVm1M+8w7F%5N47>-zC$#6{9<4Ypo##J-07L9S!cKfZsUa^CB)2GicD8Zubr#l%A z=WE&LWPrGadL$ScR!k@D@wZFd4;Y&Am(3N^C_e-H{TdG7DTcOMrBME+WpUq6cXxJD@k8yktIt$5nF`SzaBS`dxjJeDJ-weMa0rK0 z9MO=*(f`P2>rIB3WjYSSq4pg}j%9Wn#PJM6L|jQA16@T{!!dL)?5$Svm@iy%Jud{g zKDyI&WiRG3b8#5d&Iflt_~nz&H{O454e^;Oxvn|y@LXn2#Rx-lxB<2>Mkfc~vG{Ue3s>SZrr|vDkLLMgmjozZJK-wX zmh;~pBbS+0F+qtx+Q5HPW0-mDFb|ob5~IqK%rs##Ac|ZPi|z7>($YNlE=V|tP73yd zYX&8c^r%;&`ED!(rgepAC$vyOw;Q+7k&2mZ6jEj$nyG>)c@`hCrQcm!C*ZiePtgZm z%Mm`&)Hn|oMNz}+c!P%Y5+^S0cZ-EZah2iJ*1m1BTDhqIsan>kjk+%P1{~A(xsNtt z*Jdz`BbH1`k_AW-g@%&&F_c8@#4r(h_h6FbOQdy?l+@FzXV<{=V{1_Ljx{vs`(kTo z(r+WRO8+vvOkag0ly5ObdIT->s}O#rpL^sbMsila3AT>CB-GtFcnfX$Z0hOgI@-@3 z_zcM`oj0V8ucPxC8nUVG)G1OY%6EG1pl??1xQ%nS(0}n7l6iSfZ&P+1qpLDFMrdM( zDY#5Wmt`v7A*FneR^cD#l+NlVe0L`*(?W$_QO^z{M& diff --git a/_tests/out_app/com/lanremote/watch/MainActivity$8.class b/_tests/out_app/com/lanremote/watch/MainActivity$8.class deleted file mode 100644 index e317ee12aef8dfbfbb8d1ca3d4828a56e03e8e93..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 649 zcmaix-%1-n6vn^Vq}gO$%^$0^t<@N|kVJ635D}C@2`v<2Ewto*GCP>Dn;F=hM7$LA zfeMP^g%99EiDzh`rCwxV&zbY_+i%XE^YD0g4`2ta3Op2i6hpi~iJ>LTI8M3Fck@gZ0Gqp)hK>{+zOP_iSHN83dAXW{z+%Bc7VLd+p#sGaaBcNZMTN5c~-EV&&h zt?UPeLc22vuz;$MT8KIt49!{o3;`EH=6STcO%1(o&+e!*@bQvigX<(SDv75`p2h${m`LVi|` zum3y3EjI&l{iH=|a;1+Cr5%|h#5*^k`f#Gsq-V6I>3ri_rVQa1sS5=+vUxDM5Gh8mxN*{})yCR}@Q>;hj@_g_S>i wMA`A>?iJWA=Ks=x)4dD$-X#inO^k;(cuToRD_zAJof@RMuidkVb!?O>Pp_Gn&;S4c diff --git a/_tests/out_app/com/lanremote/watch/MainActivity$80$1$1.class b/_tests/out_app/com/lanremote/watch/MainActivity$80$1$1.class deleted file mode 100644 index 23c9655e26d0b31c29de399fb12799631529332c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1176 zcma)5&rcIU6#k~I-FCZ{g5a-;Vp*guNEZ-67g209At`Et;q0j9YY4a7!<+&%Cs~2;#nqf}suCZ#k&*TY39+rn0!fw%Un>e6X_$sP#Slzq=A($CO+Z3IJ31KpS^_zsMg00&p`}LA zi7o*rC7i-(hTaxX4lopkR8tHBh~un;B+fBJSGAJt@~5st@(VTnW!EU>3WnpVF`UN* z0X-5fBE=9tdQ3T4;*_GU^h~B6*^oI^fP5sObFOWeOCm0zUqD(y23dxlX4)8Hnyz!l zk;g_AQa7G%F0X-09aPQCM=;2c^mewr_piLQuio0H-SwAyTOYjjk9(EM?&eRQ*pP&r z&q~~f99EPFsBB4ZzD%?M{%DqP4#xW(U52Wp^NarWM0A8pAY0h|zc*>c0OY1Z)12*$P+%$16Bd zx6UNLp*w(I=&hpf@UUZ`ipvd4rHYY5gbsuVA&zdL4TC}gs?bjNPZD+jH!wwWh#KG~ YrpZc?-AS|m#p_dziEoZkX>W(cf2M^rhX4Qo diff --git a/_tests/out_app/com/lanremote/watch/MainActivity$80$1.class b/_tests/out_app/com/lanremote/watch/MainActivity$80$1.class deleted file mode 100644 index a3f83f717a979defac720e6eb65ac5c0ee14dae0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2076 zcma)7-BTM?6#v~M2_FGzOKsW;1!~biwW(CVuO(TNg=7By)kD+38#o|pB7!13-t4}()r z%bLNo#hySTYIt!8aKppko)F7`(9=abAoRbU71=$(n8o3Ig_&1m&!xc~Ao{ z$36i*)RLB|L{X+9=ci|+gii6CloeS&&0r6P#;Z|}29Es#8qvhCcef7)E^EWtyuM&c zPrC6mnmG;#IEZH$4n09W|K;D!&{{G7s0%F&2k(EnvVG-~&BgDxmzTG0+}pnQ`PS{F zt*;h0uYI(6{k8{(5a4JN@GK59_$vj$;1LrENuwTgQxtu{%3%~sZB{L=tFn@chQ>KU z47H{D45+HOU^K#mAiPE$I|Ov1i@~AEA4q1wMvUuj0X=w*{--3WetmExRA#=|lqkoM zUA-!Lb>SET^SE%Fh~I-=oDdMjNotEK}&lKcLQc^wWhu2UV5-^Oj1XQgst(lc#Xb6t%N^!?W zKh6ml!As;r%;zN~$Bq;}r&%o+~Oc)CC{cC8Y}t zHoxD6iwrOtGA`f}UMEXpl0v9255a0jXwrpAhVTvqi^zL$_v-x**Z)|%ymez~^ZI9- zSAW=A{CIomy9aj{Io@D6TnUQiMhNtHFom~_0ezc%REDFeMF=#2FAA7Of}z@i^idPj zbCr#h!@(+a5g?_7QgJQpW2z$N~JT4RzMV!vi<+;kSP(oHXWzI{JWp207 zI1SQZE=d+^VzMHQ6|&Qkda>xk9hXyzs25avYgY>j!=AV6f=s*(we*jIrjsnNK6+b4_rHag!Pim8@CCfYvt>Nx^RJ=x)&_zTcMy)*Pc;{1 z+8MsaHG^+nN5qDo(d#?v>s!V1W1Xuw-To`uN@tO^nuGrYkN3*1EhC4xmQxlZ? zn`?N-NZ)3re~d~9=R*k2M`+|g6Stv-+tJM(80J+N<4&Zx3tw_KR_K0%SL0V+i&?VJ z2xrqjaC6kNzcIwo+ciMPpJ;&{8Ph`+3Q-4r;&R9n*J|>_&|@^qc&3U3b2N&Y)AKJi CZZjVM diff --git a/_tests/out_app/com/lanremote/watch/MainActivity$80.class b/_tests/out_app/com/lanremote/watch/MainActivity$80.class deleted file mode 100644 index cff6676fb15ddfe5999352c2cd38a90bf288e7a8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1515 zcma)6TTc@~6#fQEw=An*0l`~9tk{-IK~d4g3t$njTx&4sgITr{8QktRyIU`Rz{D5* z1tz|k_@+T28ui^@;S0uhAG{^TGo{8-F?Eyf%$z+v=ljn2&bMei)%+;a_)d~U{ zY7k^NWIEY|W!NsyIzCU_HhgnAF=YsQ*!0CM;opr6_AvzgW#L6=(~+v=ud4{5R)MOa z4jMzligC-38EzzImR7jwQ?wyr3;!%bbu_l1VkhbqG-%j`FhhH#_zZQ1X>!kt4D|I8 zqOoXYNU?2Q<_nZZEx~PH521~K#Dq68e!Cnpfu%DrIJA{*- zW7Gegr4@!q^jfN%v>w|u<$YmMd2@OVb%UPX4=UPmP(g==Lx}u?e<@OifN5A3!@+1- z$jR~?A#^g-`23FV$tp)R#1Jp3-zizYW94B=u%~@j*f+LdPf<;%Slu$P$nrwqUJVIQ zD7Dy_K^b@Ozxra>8P^e+gySW~X>V~?S8$Z!@ODKmlVzk|ML!0l&Wq1*bHehRzWF8zQwJ=XfOKmSXJ+>YmTrwy|V!J$6;W8HUD!285HCm`UC- zdCnJ(9l|+=`i=WfKdr8QeDrSP!M%-#&t-3hHH={NPj8MNl{HLN=2Rq9Ku~PgCZ(;3 z_1J=fB(eD;%-?va*#D6xwm_^$oeU@ZDPi;Je0GVumnC7+y_+ZX?@0TGd6UFlw5n+* z@0xr}NS9ru&FhsYC{WTiEyp8?O>uwO$!M67H%5InQ+uR+s4A0{p z%}W(sF$@>L8XDv+Msrj}zdDSO^&;&$=qxR2yz2#6d=1JoI*~@`9i$b(&-9*vR`h>N z)&PB*i?4#MV@J&!gchsAjp@ZexH-L!mL?o{iMCW%`0zT8Om&63Xqn!0?s<*=#dvqm z8jiihi7QXAGbKNS&yvsiXEGUeHi;nuB-3}ImePMgAovw+!Efjcen(I62S$_@B$Z~2 rDZ4SNv|^Hc>8k!hMhvN_#w9XTA%!Vg1EiQ~%+fAgWJ+6_g~Ia-EBTe> diff --git a/_tests/out_app/com/lanremote/watch/MainActivity$81.class b/_tests/out_app/com/lanremote/watch/MainActivity$81.class deleted file mode 100644 index c167e25009c3d26a17273b55e3bf38a82fb1bf90..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2403 zcmbVNYf}?f7=BI&oDi0a0WVbBqQ;tVF%DpC{#LuiSGg0*(ptgx``#@!8SZSBRj zcKV_HR;Ny9I{jq7G9wr3==57J|DZpB8UI3OI@0$fp)81l)173`*|YCH@9lZt_s^%l zJOqlux&oz|Uj zBg3KiPV%x)5Rg!eAVcxIVuYt`E0bmj#b=axMd+G}C0$!Lr{o|clya0wC`ScD#fHG} zFjBkzXY<)CmWyaNw zSX_5pvN42x5~}f%2d+`T^}P_>c1Dh5gWYTBn(4{`93c19xl{nJFi4j*#m(5-C4-a) zQG`Sc8sb()ZTn3`00_!4u?M3}vrJLrPjMBp%R}s0kv1C`YY?I@CWW zoL);A{HkIY3<&Srju%LLG_IH_+tO1p%ZZ((y`kAM$7O~i+m+Rp78q^{Lc_FZR64`io%K=M zl(%wi>$B)+voJX3B$%*>49}WKDNkF1S$n-f8_AfaGHGaXbb{l`I$w1wHZXkVnyRH; z-7*89P=}s;v%LE4cdK9j_~h23)vxY6`RQj7#f*e`T-~Ujwrqcl@9b9ICvW0&UTxMb zM~Mp8*V&zwUN@Pc{eP*6j#4Okt(3-49@kARk(ry+?DHZDG-s0fl$q_D#2q_B4Y4%o zD(WniKwcu4v@*7;_31)@ANUdNN+_)|D*Ok*a~+2hQ{1(Z{Z_I z;{~Dp$#H+^<>U%pt-_Hd9F2?Db)kk8#8%KU_7Lsk%Qz9wuS0p52k2x-G(5m5hVOBT zHhm1gVzARs0=(KEiiZ*_cykHo@1p45+Fy0m{s)-wVXU6sT?}2Kce&?#Yme#^IZ)*x zwDH=8x@Al)VP@k;G)DC5UyX2sCX zo6yZ$(9c^j&D(H;AIDAJf!q89?(k0Ba&D8j9MV z;s6J=0Q&X(BPQYC-`KxKlb&(XOt*9;<>eES_Tf`}MysDn=N7)CU5L&^7R9CImVW^n CDvMzN diff --git a/_tests/out_app/com/lanremote/watch/MainActivity$82.class b/_tests/out_app/com/lanremote/watch/MainActivity$82.class deleted file mode 100644 index cb60cecf5ea58c76624e5cd9e22d630792f68892..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1376 zcmbtUZBNrs6n^g5*0EKA@eaOBnAnY%0V;w66vn$lO=gITpStd5D|YSDb|d~Q69%Z! zAK;HNp4%;K3Wg6hx%b?jKIb_vJ?H-X_4NmUC&+fghb{?z836+Yxc@*+dfE$dEvo?#+;6Fw~*gk*Fh%n+)pM%+{@oFSS$R_m&8 zRZ8VtTQ^G{QkHQGw;93$Nvp;khTbN&=ITZ&t2?fT?3K|cIJ*VXPz&4$qaOnj24xIk zn5^0k-V~0x=2G0Nx~aSC3|&g%D1=dlfafEEaomw`SH?ZuXSnTIUb-WAiIer9vY+UP zx=ET2;{hflOv;GkA;ZXZ1vzjmlUS_6F+*JWaFwLR>$yBst7!?37>3&kG%K`O(~Tmx z!4q=Xhimk;2*Sz3b_=}>I;=d_`(-?mzM~6ZUuO;HL<s9 zCVYptn4_%*x~!L=m-kJR+ZjW395Ibjs}P9MWcPz%75?5{G2j468edP+aC(xi6}oAB zw1%)w@&%n1Nd`!cC8rx;$(aVEZOWK1ZA=%iVvk$}cwuubw zg2#a5G4CZ$iRKhi;5+*BUD1(T-cPTQ@h=$jA$Nh;H%#Tvp^&oY42kG0ZEe6AQfFAa vz*5_56n^qr!-`bL7Rg=d1YOwjV%WwGNk6soE)M7vr5Hqh;#no_t6=yyL|t7S diff --git a/_tests/out_app/com/lanremote/watch/MainActivity$83.class b/_tests/out_app/com/lanremote/watch/MainActivity$83.class deleted file mode 100644 index 32d36b7bded0b813ca47c7c445223b7ff3e85bde..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 798 zcma)4TWb?R6#h;UGwo*EY-8(PThndL1x>+3q*%+vT5Fh5uIfw5%mznR!&u`xWY@+o$Ao;X-gB^svViAe36;O#U7KigL=ZaN8^!i?fSrbi}I1_TYKdV)0U4d-#)bcW4*e_5cl_NPp>dctzQ&hQv`z=5=b)eL(|b;i(`h& z`o4K+DyH3X-EZ4+Hwe@;45`4uUf_cIdPGC|4=Bx+P+&&Gm;w_j(lHytNn#wAIj(4! zK$anWM)16`FD##AFWb`gA2CGoh3zD+;X20+4L32#Fmc{Oo_VgMXt%M?pyywOG@qob z6;7azZ#z__gIWr=FvT&gLC0-|p+LjwTgr6MAsN+tf1-Y*mpZ6Wk1dHA)9(Lh95dNOq)KJ9(f}6H?pvG%e^w={!&eQ!;Q|Yxe zDMfq5F+ERs#CBFg=X6D`)i;OgcqJ*7%tk&EC3xs0x1g^hl}owMv2^*w)!G5!vd{5{J210wX7 bQe5}2L^etza32rJ%MhkkqmB}xwRrM3PW?JV diff --git a/_tests/out_app/com/lanremote/watch/MainActivity$85.class b/_tests/out_app/com/lanremote/watch/MainActivity$85.class deleted file mode 100644 index 9c486b56168b2e87003dbb4d6eddee734945b1a6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1139 zcmbtUe^1j;6g_Vnd#n@~QxNdigsN=^3{Viom>2>P1DZHPME>f!7btY?(!OE%DkK`e zf(cV%q94EyWxTH}GIa@m*re~?ci(yE-kx_~fBgLV9l(7&${~d`M@B~vvJ8`MSTFld z5Q%yyMfsg0-M#Xr;{{J$=^c9V$XHrn$jUu0Hf9+nHZH<1s|39|a?luh4;|kKoVs8r zZ0tLSj!Ly#uE@v>b`zwoqhBGpLLP`CwR=!UUd3`M<~vp4Ybf9n$7LPE7-7htQ@yP2 z3s+KwkG;T?PZ-jswWVPcV;ti;uHxDs@UzU6&Wl&;4SAH{-)gvln;b)6LEzXkE zZ_JCe?x>=z>0d?s3$V60Y=-HJ$}!f1MpHg>f|@TPhMoVYrt{3Z6~vHP32W3NgBxBT zUNq}f5xsV*K1FgBZ#QscGa{ddK~fXLu-Toazaky?fW&X_)+%8$a>bgbsD?Vlo>3h{ z30DKx4`XV^O(FNfnvO+0pm5EL8|o&kiFWQnJLB)uwYC`a^&k+@itog+h?D8LVklB0 z7rjyGo{KY zu9H7-ib+MAN@xd=CffJl`~yt>5p%SX1~X}xPeAwRR{~_H#TT$dUV%2MTmT7FY!qKk8?(r3#dGTVom%jEO;;V5oh-5MPI7gsIDHvpZn?O(q%> zh>Z{a0DqM64mByEO?YsV-7_Z$Qx%6zGA*ly4D(y0?x0dniCcuh~6w(YUWA2v}9|{JibM=H4Krn!KJN-YjVy8&@-`e47;@*^j=IOf)8yv4R8qfSh?gs!G;ZLg zg-K%E|lk@}yim{DO&#gi6Q4Lk5?tUKq%p>jg>+rQKJO`$4}+lkfxgMCnL1eCd5` z$%uZ7kfHe>e&)+l=`bW3L63@;ZAm5Gj)q+kzT;h=R#I)*S6q)m8e74c1%}OXYl+=@ z)5xaV!6@{^fizP2`Q+}Ha#O(9%JYMWy5vykLC~{N!!|AVWOQO$^g#TJ?UWZ1Y2@vu zqD(Y=9z`OuKs8!hVvD2~@`xh!aTe4Mi05*qodH5>))~^}q5Bl0*TFjB290Wj3Bv2u zttnWwHicE)o+4BIfotDsouNeEBq6LX*rag`1)4p=WBM8em-fdA3)Kk;Okkao*aVqV o+&LFWo3PebIMz2JmuE5+R0$K*$y?Z=kwY9~&OAku-Akta0vy2zA^-pY diff --git a/_tests/out_app/com/lanremote/watch/MainActivity$87.class b/_tests/out_app/com/lanremote/watch/MainActivity$87.class deleted file mode 100644 index 91306927986f12dd737e14f899b441527a507cc1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2075 zcmbtVT~ixn6n@?$@P=et=u%6m7HOMWlF%k)Xp5wxEdm8Yp`=0sYTazMu&~*UyBpLy zy>!Mu;H5JBAT&T0PqR;*XEE9)J3=h&Ul(5>wO<$F&)zS_f=QQQw|g)-a|S+BDm?>@Z$qko<&? zKHGkL_jA#^SqaxMM~9)-l@ZYtVneGw66F-LXj^(QWjU#9q#K%@;kZG?Xyr@iJ9a*T zdE~@7y2&uwIv@FDn(TNnf<-KG+>(&TG9CFwP*n1=N&npF-@glXqosj#~TdY z-m%cF)b!lMd#YA;b;}g>c}v1=yuHub3qztn+17xYp!M(#BH>(=eWZWv!i0sVjrgptjDrojiQ4V5dPG2>>IYnHgNktPQ=PK$34Mop_gN9Ju z;PZ(_1j<=4bRU6AvPjwxNij=~ zC6YT}iNPK4#MvE06TP7wbS8EoZ_`@_jn;OO;P-Kd^ZSEYIDb+U5$8kx0NweZ zd^DF2$;Wc}HuD}JN^j{ z4$7~{SF3n+3(-OOnmj}5XvnwE-p72N;O%1J9$sJCLRVJ2pu00!#hcp#B+78GLLdd~ zAxH%KYxMDNkmTQDlz)e-{CiCDA8>>Jh!y@5%KSGt{1>?VSA4*K#~S(dA@qa{aE5+t nibMYkyx;k zwsh*&hAq5RM~XusKO0L+3@KR`p|Qv?S&7pxDhE9}(#SAm4{g_|+g{b>0mFQ(v9W#R zub-($IJ%A-xXF-KIC~-pCBsOC6arsVts_ybacQkph1}+!WMq-k(Wfjj$|B^B?^QFn zg#itNI)*UJklVKpZ549O+T7je4nd}tg(u_+L&D7OWH5qJ4R>_h#XW}MYrHXNynKr?m z?Qmn$E4jk?SP>yzt6G0CV~bsk)##vJY$y0~)vvwC?=U1wew8!yR)ohtG#k4-*j9C+ zj@lA6&z8-A)|8ZpJ;SuwM!P1`Vp+jvw)|$`@b^MN^>vQRf+{OHT=N{)52@H2T-N=n zh8GMIai!8x#4>R^2>ix@q*-9t`=>HpE!=WXO2ZU>qyWSFbcmYek2L;6oc9yjFFt`0iisN@Sov1$t0HW60c}AN;itT`j~XOlFCrLud$j+ F{{;@@dwc)@ diff --git a/_tests/out_app/com/lanremote/watch/MainActivity$89.class b/_tests/out_app/com/lanremote/watch/MainActivity$89.class deleted file mode 100644 index 3af434a3b2b11b51bb331fa054df30a74d723056..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 559 zcmah`TT22#6#hodUDquy-LHrls6qNrQ4~Q)L7){WzK`pmBkL^e&Kmu!peTCi2lS(& zvlQ5y12f;beCIOfe0_g>0yxA@3NgeDBy7wf$*}8qz3jX60u{bH@=uP&zuUQ%8H{*)3JN29_A|(^eirrFhR342g!sGv7I9zH|Tj{`ds2hpj9$=mt_2W{_s6xcGZbz_(T=2&(J_Nwt9MPz zA#b2yp@B*dJrqhb>qi@E6!xXE&d3`K}Ctsw)3d9XquSp{uJqnksCtcxU7NKY(gW9JpD@do3WG=?R5(}d6- x=v{{O$FCBS6`95h*gIw?Y%__YJ;KnQpkswN8dkAJn4;;gW0NG4e3NW+egGi4e5U{a diff --git a/_tests/out_app/com/lanremote/watch/MainActivity$90.class b/_tests/out_app/com/lanremote/watch/MainActivity$90.class deleted file mode 100644 index 8d36c8127efc9e11b6c338593df0840c290a2e69..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 652 zcmah`T}vB56g`uq+0ABaVyqw4*2Z*&Bn8(8MT$}=K|zSMpyYirJ0zosQ0irgWT^F(mS3imq0BG&FeSvx{!kfK+Wlujhn)dKaFI=OAy1U! zFaF!a9oGa>J=P-4jMAesX~$U-;;m~@V>nf5av`my8}pTGnKFc@nhNTr+!$#*G)eJU zR#N=$18CBCeK0J6)$4A=H1z`QDjI|?Q`DlfLxS#yA7I@(c(-(-Z;>sNho6vL#o7}; xBJcQq_dD26y!^`!9PW?c`C}CEnm7flV}pE&hWZAZ6g3EQuiaY0J8YFJzX7jwnS1~M diff --git a/_tests/out_app/com/lanremote/watch/MainActivity$91.class b/_tests/out_app/com/lanremote/watch/MainActivity$91.class deleted file mode 100644 index d7980a694d3826467a902ded3aa338eca441ba9e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 832 zcma)4OK;Oa5dJm^v6C2@1}JTLg)}Wm3OJQOfQl-i2qdJGA_e8{*sErXvj%&Uid!#S zc}U!Q;s7U#s2UZ)58!7YLirVlSqLhkhalOrGqbbbeDnDHWA_t)%ea_A3~>Vq3x|+o zm~n%S?K?8$oj`GW-BE7aUUr1M=_=6^>T&V<0z*=@MO3UZ6zZem7t=72GLW{AfyIzr zb9#=>uxhWg*0`$(?S_y-EiuGP<)(=v3`ir3aRWIEM{$fHKPn4D+HqYT5veNCx>_2| ztG-X9p+X^77t2kZcEZ95OfqDKfLE?*#M%Eq)a0ib<_0_OzV)B%z241#*?37`{_BTV zgXd2N&)$6BemmIO7;J9teSD#Fow9IR*Y}6~LbYn(EWI&a$aHZ%by5~slGgx;d5vuu46!FNwJRu&)N{Igy zfIKap35GLZ*{ohZ+Q%6E8BCHlPg;R&?MYYWx4|kqFt*5|?vPB<3A018fSF0>a%ty$YA*Rd-aTEzThFQ$fnV|WcN13$T5SLV9 JTfv27>L+Xv)u#Xe diff --git a/_tests/out_app/com/lanremote/watch/MainActivity$92.class b/_tests/out_app/com/lanremote/watch/MainActivity$92.class deleted file mode 100644 index 507228e9d35889c4906b1cba100edbbb4db5d719..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1217 zcmb7D?M@Rx6g|_H?zUZ|AcBG)P>OC_WC20IB8m+rBt=L{`qyl?BOP3J$?j6*xAF50 z{O=2BRKOoTfbXG+FJZg`SO}ODH`%%Kap&AK_uTva_t$R##_>Qwh@snbs-7#VhUGc| zcLJjz?Xw_;2wD_GH7HOS1}t~ouuaF~>u$h}Eil5(+d8tvJV!Tr=yYjIe4fRo1u_5Za7zL@>(`)-z=lNrqU1K^z@8ub@-I z1#~g=H4Dekxypl5&GdM6+2c>R#~q9N3{!gXC~_(AgtMAo$P`az<A+cc!BL3KNt2}`|^eaed7(kjqZyNUyiSjAM zt8<3gf4uoWC`ks_HQbQ0wU`?l_O?_bqalkNaWySUte>8k8lgfM)SFj=6!#{H`(x+h z&)pZl-o2M_hK3Q0GQ*^NwY^ zK2_Kv4{B~z!;DN}M`csk)e;W^I`NlHhuaL=f> zgcOQUe=&@KC6ls+spUiTw<1To<1|arTJFTNxz7k^cM$tPD~1Ugqhz5zr|~u>5B*mV z-uEBMenPmTE&K)Td*~+mSN3o%ggp#0$fI58`yNBP7(PPmLJJ|jM7#P5z3OYE)Hlef nZwYgX;)O7cJ7gmyd>(gc)j!7mfV^8qq|j diff --git a/_tests/out_app/com/lanremote/watch/MainActivity$93.class b/_tests/out_app/com/lanremote/watch/MainActivity$93.class deleted file mode 100644 index 688ce30df212f3e4c1727884628c555730765066..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1468 zcma)6T~8EO7=8{QGps{}ZHuj6AS`xSg{g)5wQ7YGTFjy*7#c5nG0dLs4jpFf%vosf z!1x&zZ8S77esQ6(A4#>5MUmjWf1~cQd*wf9`kumqAtCD~d(N4e_q^|Op67Yb>wjnG z0d(M470OVqphCksR5CP~?yznfj>m^x!Szu?m?^!_u$)d)SRp;DwQKlC|6 zW4u&+GyoM<3IZC|Lu05JGDZwpA*r7j9O9;+YzHmJ5{DVe!z}{}K47RfoP_6E3Eenz zM(?z_CyrW%?IuHAY0FM&3$Da|^eo^XGnfJ9l4Z`dC99HZrI_7pE*?bB1U*zJfEBGQ58NW12fA zkG3pVTLW8E#itD2R5Lem`OS+f*&7puN0WuA>FlLn^HYzrPo5H%H?tSs&Rs3s8OuK! zFI>Eqn|zX=zqy3&!Fc}Fz1+mZf5y&AbelD7k>~=1u8;e^k>o125o?R5QqaKATuKE} zEwnp;dNfMbni#_2lFHE@Vo*Y8*02MgG1L--ldf-(6L?oz2-jh-*JNs$sG=;38upSl z-uH&dLnoY=ZJ9sBEuU25-g{BoLI=-YpUh=0NnkrQ>?#5~&XdLpF!{AjgAOujg`aXq z$p+yywTmkFf?<2nMyQF5+wgtvE9hj{SR`YW&FKV7O{m}~gI)}KPmvY$ywl!mY@!pc z@`i$5qM}%9nJzTY?WKZYH&{)Lyus*FE2IBjH15IPrTFhCi;Qo5iR#mlXJ9kf5DaGUQSg%tHU+o7MEz;XvOR-PFw8QDWYGHU zO3H0eDCJ#LtM?F8@1tIwLbLii!s;K`rT&R>?4#sm*pCA=S0IW*_=;AMrLwdn=N>^9 Us;FBRe=&69>&hxR_Lhkq#)3W3g2hfK}XhE*o)E63W}nKen3Ae zI!l4QIWXrt=gfC5bI#ZI$0vXe_H@J$*N`x=f+WMf?ao>bSDu);foMJQz@D@QT&iE>wzJj$lai+z~;xFO|3s zXO{3Dejnmqs2GfUH}q_ADI;$F|6q=yY!rB*Y{&Jb8V^J;aYrV0qHkxcP&%VNSGQaV zhrzs3N_aho`@Zl|p(Uiium+aPMT?-_#OS6`An!V9Rk9h*};t6%);&@(w|jm zQGD~{65r$RYv86${1PI?#^4 z`tK08+z?oG-^es8v^hCcc9JC_Ubqo0k7qhfSoEtfDkX%U4firgMNz22LyN`k6=bph zdPj>lTLWPY)M&Wv^1@4e>u55%&QY6lhXlR1zd^loct1JgAF-{nhu>rS7*Fo_5qrn4 t^?rdau>6-DINW~+&)-5BPnlD~GivO2LkpGmAp>(i4rk82bMNKVpT7X!qV1!EvWH5D2dENW$ZQg)!sKd_SruOh zE64G%(B?o|J=6AEw7Wy7+OaO8Z9;o6kG~h72H!&vqK=TzI2SYFDxAe9qjM!KtLw7s)t6VS)t9@v9jYV39;cuv@n|LG-1(?!l;xG9vSYXpNgVTg@+c4-7Cmq zcX~&QH(LW?8PsUF?efA)eCud3`k13O=MD*auYQ7hzuyAJ8-!F8J_~7^8QpUDhAnt>TNg$;n$%5e(h%mBDRzQmBMj9dZMwmJhCrg6eas6FR0UoQHP9I9 z*Yz!3W>||ZtgdrIP_%K=GQ|W#WhlJtg%5QK>NPaL&(L;S{AtrQ>@Du>kgv;i$4E2u zgc7=ya%?jdHEc_8OGM*xzAs{P;Y2=ZR+yP+!gNJUK@&scF_NXUV{hxL8Lr^$ks@zB z={Wk1bn&8Dp5h!s%dy*{a7=4$Dr;s^-0`9nss|St>b}2t{Ui7G+q>PLx%a<5zml3u z8ZM)q@amhJ+)6R@h6*k`im|DPtq8~v9Va{sH7w)Vsi6y389c%+WMXIv z{hy4ogb`8Dt)U0K4DF}YzLX0LUfnRb>jnq=i3}s5@@zQe(8~&<44vhY5%GcpUi4u= zGHH-uw0t`9iJk1dq*xfk8m{3wL#>mw7OcA_F@WpTmX^}C+{s#&WJN4Y7)KfW$E`n7 z5%BUb$e*DqZl^erBVk&6KD)8Xoh2EYSWgfL$fD4Vdr5sWFI8}h!S^p~soINn)-m|3 zDcu@MJJBaiDfoL%~=+A;u+m_xr=ss0z9Dp zfz{W`XMkRY3i@j>M$#L!3ea9kypisYV3B<&yR>77(O6A0)CV+RVUCBca3eF~rY2zBaXw5U%opgzNp`W$gJhnr-lp<-S^<8K5MOc0*iB&)zA Vrf9Ar4#Y7-D}V89v?OxnzrUdZSOfq7 diff --git a/_tests/out_app/com/lanremote/watch/MainActivity$98.class b/_tests/out_app/com/lanremote/watch/MainActivity$98.class deleted file mode 100644 index 39a8aaa43fd1e699418e1b3a16a9b76b66c9d1d3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2276 zcmah~>rWI{6#rfJF~eneEzd=)6j2rw7Fr9++7_ffpn%GvmA-UXZg&TlS)7^0VjtGF zA6gUBr0JI$(?`;TCjHQu)P)wUjft@_P5Mu$wI3S$q2HRgJ$H9;G0?Ed&Y649x##@O zIlp`6_QRik2CxGkC@5fPh$N)#dVP{Sby3%5LR>1}aEfd2OPVmU+kb;d2HTkGnSbLI2d{S5(^(d8(1`SW5 zk+A49Gs2u?XbY@PrDfGt&P_-~69W&DB?-dUpD;}-oslpZ>Xv=x^3Cf)3OY&us2B$vXNC%rH&F33 zp*~^@tF13e6;<&JL+Oxij%JC~*}kK_gQEl88g^nAwbvHTs5q4rwsXiLAeKWtZ(alQ zszQZiXFCwmup7@(56MvuhiqdaNi=W=2C_!aYv`3mC7EdYWX?jx9%}Udy<^hR3mW#y zf|cbF4GSlhn3T%>8V+E9Qi)2q_DsUGMSo&aWOy>5;zfpvdzUWUJ^%IH&%b?e_2S*7 z)cxyUHpr$8OCF3c)CUGvw<_Q5Lkvp8z3;#KWByYQT}=A1?XGm)+3wuR^`R?$F$!1u z_r`V=tEbiyl`jn{DZ3>aWDuCZy?H0q(>scwwt%upWxZ)Z)1|4CV+zxfcc=dC)v!5Lltp zovm4!!D!MnWjTm>3W#35e5saI4BhJ^Sp!DPQDSSBP^CkLDMpgh6T%vk(P$ioji{+R zNlVg=020iwqeo5->oXY(1#dq-)-q#~S{qIzt%%rXNGIhhr+K?XPHf(5M&b#ZW@?t{ z8qP^d6h&epaxyHeSwSVy252%^{c+v4g^dOp(6<`=Eueonk|by8 zt(l~YWNGWRD{!@Lzk2g(2U;toM;>wGxLvy9Cu2o=1gT zjb&Cx=i%|a)a9N>{qo^sbfij2;blTKB6Tv>Yh>5M=drcNvurm(c7D$yCQ15Yxj53k zt;bX4?($NCt|d&3xB5L*-UY-{NL;20UBeCB#0e_(E!-wqkg1}H4OGR4aC1QA1!(3j zv~V{%xd%Pmi#=SyAXjmi7vdP_IL?a@r4Q{nUW{*eDSqT-xW+4RgID7wuf#20hugdc zzwui9$^H0?Z@}NY9{=!-OySKqL)|)q!ZbdCs=)15@D@{0r=Wwmf#N=vCWd4*Sh1f* z?ONvh6`V*@$=-qs+XRY1F1je*VY)4x+|l6nPILz%75Jej5$cbW=IXVpO;1Lwb)7xZ%`scUpVGquAsNsifXuh!=`Y8t|z=P)cm;XNA%z2vha*yc6g&JI8gdal|qgN+X2HO zV(GM*Z-6AdB6?Uc`X^8zStM5yU%VI3oLjOk8FAf2UWNX+=%3np^;(T&b1bPDEkcEPNx4eo$35CyJJ!sME8=RI#gO-&u6}j?@|>8 zdlb#%KwhZyQOj<6n6Ad)Txu=jK`4?$CKk3BTK{>ZWXL6s$VrYu&B0B>p~_8d5i&Xj z)ClWT9jA#CR(r2tz5OMu-oX;9FW= diff --git a/_tests/out_app/com/lanremote/watch/MainActivity$TabDef.class b/_tests/out_app/com/lanremote/watch/MainActivity$TabDef.class deleted file mode 100644 index 1b160e0bafa7d8613ed8f64df8395e152a860612..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 634 zcma)3%TB^T6g>kY1*}jWqT&k^5}}}V;Z9;=FeWA_3k(}pLmjb%wx%r@cm9$KV&cLN z@S}{kxX=U>+{~FfbME7u`|MFC42*VvD9wN&*03Ba zIAw@ds@(+QNGM3EScb~5Z@PBK_N46w(s&esd2d_^%Q-UxYhndYK= zwtgCP0?%>=Ct=ByiZnE02{Z7iY_GMjFm;%vW>w^nr!fbfJAR<#TyMSyL%TBn`p+(Z z)T?%B)~0()iqb91k=J9pFFn#aqL*aH9ebv{u)-ko+l|9eJcD}aIMQp5gzrmV!4^Yl zkzCUE?WBTThVmkMncSg;(TO9nH$uCPLJN0BH!bvfhGZNe!!s>^m9RijgV>xfufKxn zwJ8+6KE;ZDFh%BN#$KbdKpEf_a9%}%>qzk$a=cEt>+~DJ28x7Py2Y`HZHjgXODKK; Dh6$Qj diff --git a/_tests/out_app/com/lanremote/watch/MainActivity.class b/_tests/out_app/com/lanremote/watch/MainActivity.class deleted file mode 100644 index ef4ff479b846deeb41ae04c4df6f366134a55fc6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 82397 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 Q2mf{_ofKGS&)3ZV1J0I%O8@`> diff --git a/_tests/test_apk_contents.py b/_tests/test_apk_contents.py index 7fda053..7773024 100644 --- a/_tests/test_apk_contents.py +++ b/_tests/test_apk_contents.py @@ -23,7 +23,7 @@ def _latest_apk() -> Path: (ROOT / "dist").glob("*watch*.apk"), key=lambda p: p.stat().st_mtime, reverse=True) return candidates[0] if candidates else \ - ROOT / "dist" / "ykxmz-watch-2.1.5.apk" + ROOT / "dist" / "ykxmz-watch-2.1.6.apk" APK = _latest_apk() diff --git a/_tests/test_mouse_control.py b/_tests/test_mouse_control.py new file mode 100644 index 0000000..55e4826 --- /dev/null +++ b/_tests/test_mouse_control.py @@ -0,0 +1,192 @@ +# -*- coding: utf-8 -*- +r'''验「远程鼠标控制」和「一键显示桌面」。 + +== 为什么要专门测 == + +这两条命令能让远端**直接操控电脑的鼠标键盘**,是最"有力量"的功能。 +一旦协议名写错、参数漏校验,表现会是"手表点了没反应", +而用户在教室里根本没法排查。 + +== 三层 == + + 一、协议常量在不在、值对不对 + 二、input.py 的坐标归一化算得对不对(纯计算,不真动鼠标) + 三、服务端的 _do_mouse 对各种参数的反应(含非法输入) + +== 为什么不真动鼠标 == + +第三层调的是服务端处理函数,它会真的调 input.move_to —— +但那只是把鼠标挪到某个像素,测试机上无所谓。 +真正的"灵不灵"得在人看来验,这里保证**逻辑和参数校验**不出错。 + +(show_desktop 也是真按 Win+D。测试时如果窗口全被最小化了, + 那就是它生效了 —— 不是 bug。) +''' +from __future__ import annotations + +import sys +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() + + print("一、协议常量") + from lanremote import protocol as P + check("CMD_MOUSE 存在且值是 mouse", + getattr(P, "CMD_MOUSE", "") == "mouse") + check("CMD_SHOW_DESKTOP 存在且值是 show_desktop", + getattr(P, "CMD_SHOW_DESKTOP", "") == "show_desktop") + + print() + print("二、坐标归一化(SendInput 要 0..65535)") + from lanremote import input as IN + + # 直接测归一化算式(把 _screen 固定成 1920x1080,保证可复现) + IN._screen_cache["v"] = (0, 0, 1920, 1080) + + captured = [] + real_send = IN._send + + def fake_send(*inputs): + captured.extend(inputs) + + IN._send = fake_send + try: + IN.move_to(0, 0) + mi = captured[-1].union.mi + check("左上角 -> (0,0)", (mi.dx, mi.dy) == (0, 0), + "拿到 (%d,%d)" % (mi.dx, mi.dy)) + + captured.clear() + IN.move_to(1919, 1079) + mi = captured[-1].union.mi + check("右下角 -> (65535,65535)", (mi.dx, mi.dy) == (65535, 65535), + "拿到 (%d,%d) —— 除以 (宽-1) 那条规则错了的话这里到不了 65535" + % (mi.dx, mi.dy)) + + captured.clear() + IN.move_to(960, 540) + mi = captured[-1].union.mi + # 960/1919*65535 = 32783.5 -> 32784;540/1079*65535 = 32800.7 -> 32801 + check("正中间约等于 32768", abs(mi.dx - 32768) < 60 + and abs(mi.dy - 32768) < 60, + "拿到 (%d,%d)" % (mi.dx, mi.dy)) + + captured.clear() + IN.move_to(99999, 99999) + mi = captured[-1].union.mi + check("超范围被夹到右下角", (mi.dx, mi.dy) == (65535, 65535), + "拿到 (%d,%d)" % (mi.dx, mi.dy)) + + captured.clear() + IN.move_to(-500, -500) + mi = captured[-1].union.mi + check("负坐标被夹到左上角", (mi.dx, mi.dy) == (0, 0), + "拿到 (%d,%d)" % (mi.dx, mi.dy)) + + # 滚轮:1 格 = 120 + captured.clear() + IN.scroll(1) + mi = captured[-1].union.mi + check("滚轮 1 格 = 120", mi.mouseData == 120, + "拿到 %d" % mi.mouseData) + captured.clear() + IN.scroll(-1) + mi = captured[-1].union.mi + check("滚轮 -1 格(向下)用无符号表示", + mi.mouseData == (0xFFFFFFFF - 119), + "拿到 %d" % mi.mouseData) + + # 夹取:一次滚几百格要拦住 + captured.clear() + IN.scroll(9999) + mi = captured[-1].union.mi + check("一次滚 9999 格被夹到 50", mi.mouseData == 50 * 120, + "拿到 %d" % mi.mouseData) + + # 组合键:Win+D 应该是 按下Win,按下D,抬起D,抬起Win + captured.clear() + IN.show_desktop() + check("Win+D 是 4 个事件(按下+抬起各两个)", len(captured) == 4, + "拿到 %d 个" % len(captured)) + if len(captured) == 4: + vks = [i.union.ki.wVk for i in captured] + ups = [bool(i.union.ki.dwFlags & IN.KEYEVENTF_KEYUP) + for i in captured] + check("Win+D 顺序正确(按下 LWIN,D;抬起 D,LWIN)", + vks == [IN.VK_LWIN, IN.VK_D, IN.VK_D, IN.VK_LWIN] + and ups == [False, False, True, True], + "vks=%s ups=%s" % (vks, ups)) + finally: + IN._send = real_send + IN._screen_cache.clear() + + print() + print("三、服务端 _do_mouse 参数校验") + import lanremote.server as S + cls = None + for name in dir(S): + c = getattr(S, name) + if isinstance(c, type) and hasattr(c, "_do_mouse"): + cls = c + break + check("找到定义了 _do_mouse 的类", cls is not None) + if cls is None: + return 1 + obj = cls.__new__(cls) + + IN._send = lambda *a: None # 测试里别真动鼠标 + try: + for action in ("move", "click", "down", "up", "scroll", "desktop", + "moveby"): + r = obj._do_mouse({"action": action, "x": 10, "y": 10, + "amount": 1}) + check(" action=%s 返回 ok" % action, + r.get("ok") is True, "拿到 %r" % r) + + r = obj._do_mouse({"action": "不存在的"}) + check("非法 action 被拒", r.get("ok") is False + and "不认识" in str(r.get("error")), + "拿到 %r" % r) + + r = obj._do_mouse({"action": "click", "button": "第四键"}) + check("非法鼠标键被拒", r.get("ok") is False, + "拿到 %r" % r) + + r = obj._do_mouse({"action": "scroll", "amount": "abc"}) + check("滚轮量不是数字被拒", r.get("ok") is False, + "拿到 %r" % r) + + r = obj._do_mouse({"action": "move", "x": "abc", "y": 1}) + check("坐标不是数字被拒", r.get("ok") is False, + "拿到 %r" % r) + finally: + IN._send = real_send + + print() + print("=" * 58) + if FAILS: + print(" 失败 %d 项:%s" % (len(FAILS), ", ".join(FAILS))) + return 1 + print(" 全部通过") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/_tests/test_package_remote.py b/_tests/test_package_remote.py index a3ed3bd..67851f0 100644 --- a/_tests/test_package_remote.py +++ b/_tests/test_package_remote.py @@ -26,9 +26,9 @@ ROOT = Path(__file__).resolve().parent.parent # 被控端 -> 一个 zip # 服务器端 -> 一个 zip # 控制端 -> 直接给 APK(不打 zip) -AGENT_ZIP = ROOT / "dist" / "ykxmz-agent-2.1.5.zip" -SERVER_ZIP = ROOT / "dist" / "ykxmz-server-2.1.5.zip" -WATCH_APK = ROOT / "dist" / "ykxmz-watch-2.1.5.apk" +AGENT_ZIP = ROOT / "dist" / "ykxmz-agent-2.1.6.zip" +SERVER_ZIP = ROOT / "dist" / "ykxmz-server-2.1.6.zip" +WATCH_APK = ROOT / "dist" / "ykxmz-watch-2.1.6.apk" TOOLCHAIN = Path(r"C:\Users\shu\Desktop\deepseekagent\starklink\toolchain") JAVAC = TOOLCHAIN / "jdk17" / "bin" / "javac.exe" JAVA = TOOLCHAIN / "jdk17" / "bin" / "java.exe" @@ -89,7 +89,7 @@ def main() -> int: for label, path in (("被控端", agent_dir / "README-agent.txt"), ("服务器端", relay_dir / "README-server.txt"), ("控制端(旁边那份)", - ROOT / "dist" / "ykxmz-watch-2.1.5-说明.txt")): + ROOT / "dist" / "ykxmz-watch-2.1.6-说明.txt")): raw = path.read_bytes() if path.is_file() else b"" check(f"{label}说明有 UTF-8 BOM", raw[:3] == b"\xef\xbb\xbf", raw[:3]) diff --git a/_tests/test_update_flow.py b/_tests/test_update_flow.py new file mode 100644 index 0000000..6b8eecf --- /dev/null +++ b/_tests/test_update_flow.py @@ -0,0 +1,209 @@ +# -*- coding: utf-8 -*- +r'''验「静默自动更新」这条链路。 + +== 分三层验,每层都能独立定位问题 == + + 一、协议层 CMD_UPDATE 常量存在、分发能走到 + 二、updater 查版本 / 下载 / 校验(不起真进程) + 三、接进服务端之后,dispatch 的结构还对(曾经被补丁弄坏过) + +== 为什么第三层要单独验 == + +我插 _do_update 的时候,补丁把 dispatch 方法**兜底的那句 +`return {"ok": False, "error": f"未知命令: {cmd}"}` 一起挪进了 +_run_update 的末尾** —— 结果: + + * _run_update 里引用了不存在的 cmd(pyflakes 报 undefined name) + * dispatch 丢了兜底分支(未知命令会直接掉到函数末尾返回 None) + +pyflakes 只报了第一半,第二半得靠结构断言才看得出来。 +所以这里把「兜底 return 必须在 dispatch 里、且在 _do_update 之前」 +写成断言。 + +== 不起真进程 == + +`apply_update` 会起分离的 .cmd 并让进程退出 —— 测试里不能真跑, +只验到"包下好了、校验过了"为止。真安装由回归里的 +「安装包完整周期」那一关覆盖。 +''' +from __future__ import annotations + +import hashlib +import http.server +import json +import re +import socketserver +import sys +import threading +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) + + +SERVER_PY = ROOT / "windows" / "lanremote" / "server.py" + + +def main() -> int: + print("=" * 58) + print(" 静默自动更新链路") + print("=" * 58) + print() + + # ---------------- 一、协议层 ---------------- + print("一、协议层") + from lanremote import protocol as P + check("CMD_UPDATE 存在", hasattr(P, "CMD_UPDATE"), + "协议里没有这条命令") + check("CMD_UPDATE 的值是 update", + getattr(P, "CMD_UPDATE", "") == "update") + + # ---------------- 二、updater ---------------- + print() + print("二、updater(查版本 / 下载 / 校验)") + from lanremote import updater as U + + # 版本比较:必须按数字段,不能按字符串 + cases = [("2.1.5", "2.1.4", True), ("2.1.4", "2.1.5", False), + ("2.1.5", "2.1.5", False), ("2.2.0", "2.1.9", True), + ("1.9.0", "2.0.0", False), ("2.1.10", "2.1.9", True)] + for new, cur, want in cases: + got = U._ver_tuple(new) > U._ver_tuple(cur) + check(" %s > %s == %s" % (new, cur, want), got == want, + "实际 %s" % got) + + # 没配中转地址要报错,不能默默成功 + try: + U.check("", "2.1.5") + check("没配中转地址时报错", False, "它竟然没报错") + except U.UpdateError: + check("没配中转地址时报错", True) + + # 起个假中转,真跑一遍 + pkg = b"MZ" + b"NEWSETUP" * 3000 + sha = hashlib.sha256(pkg).hexdigest() + + class FakeRelay(http.server.BaseHTTPRequestHandler): + def log_message(self, *a): # noqa: A003 + pass + + def do_GET(self): # noqa: N802 + if self.path.startswith("/v1/update/latest"): + body = json.dumps({ + "ok": True, "platform": "windows", "version": "9.9.9", + "name": "ykxmz-setup-9.9.9.exe", + "url": "/download/ykxmz-setup-9.9.9.exe", + "size": len(pkg), "sha256": sha, + }).encode() + self.send_response(200) + self.send_header("Content-Length", str(len(body))) + self.end_headers() + self.wfile.write(body) + elif self.path.startswith("/download/"): + self.send_response(200) + self.send_header("Content-Length", str(len(pkg))) + self.end_headers() + self.wfile.write(pkg) + else: + self.send_response(404) + self.end_headers() + + srv = socketserver.TCPServer(("127.0.0.1", 0), FakeRelay) + port = srv.server_address[1] + threading.Thread(target=srv.serve_forever, daemon=True).start() + try: + base = "http://127.0.0.1:%d" % port + info = U.check(base, "2.1.5") + check("查到最新版", info.get("latest") == "9.9.9", + "拿到 %r" % info.get("latest")) + check("判定需要更新", info.get("needUpdate") is True) + check("url 拼成了完整地址", + str(info.get("url", "")).startswith(base + "/download/"), + "拿到 %r" % info.get("url")) + check("拿得到 sha256", len(info.get("sha256", "")) == 64) + + work = ROOT / "_build" / "_tmp_update_test" + import shutil + shutil.rmtree(work, ignore_errors=True) + got = U.download(info["url"], work, info["sha256"], info["size"]) + check("下载成功", got.is_file() and got.stat().st_size == len(pkg)) + check("内容一致", got.read_bytes() == pkg) + + # 校验和不匹配必须拒绝 + try: + U.download(info["url"], work, "00" * 32, 0) + check("校验和不匹配时拒绝", False, "它竟然接受了") + except U.UpdateError: + check("校验和不匹配时拒绝", True) + + # 已是最新时不该说要更新 + info2 = U.check(base, "9.9.9") + check("已是最新时不提示更新", info2.get("needUpdate") is False) + finally: + srv.shutdown() + import shutil + shutil.rmtree(ROOT / "_build" / "_tmp_update_test", ignore_errors=True) + + # ---------------- 三、接进服务端后的结构 ---------------- + print() + print("三、服务端结构(补丁曾经在这里弄坏东西)") + src = SERVER_PY.read_text(encoding="utf-8") + + check("有 _do_update 方法", "def _do_update(self" in src) + check("有 _run_update 方法", "def _run_update(self" in src) + check("dispatch 里有 CMD_UPDATE 分支", + "if cmd == P.CMD_UPDATE:" in src) + + # ⚠ 关键:未知命令的兜底必须在 **dispatch 方法里**。 + # + # 第一版这个断言写的是"兜底**紧跟** CMD_UPDATE 分支" —— + # 后来往 dispatch 里插了 CMD_MOUSE / CMD_SHOW_DESKTOP, + # 兜底被推到后面,断言误报失败。**兜底本身是好的。** + # + # 改成验**结构真相**,不管中间插了多少分支: + # 从带 CMD_UPDATE 的那个方法体里找,必须有一句 8 空格缩进的兜底 return。 + dispatch_body = "" + for mm in re.finditer( + r"\n def [a-z_]+\(self[^\n]*\)[^\n]*:\n(.*?)(?=\n def |\Z)", + src, re.S): + if "if cmd == P.CMD_UPDATE:" in mm.group(1): + dispatch_body = mm.group(1) + break + + check("找得到 dispatch 方法体", bool(dispatch_body), + "没有哪个方法里有 CMD_UPDATE 分支") + check("dispatch 里有未知命令的兜底", + bool(re.search(r'^ return \{"ok": False, ' + r'"error": f"未知命令: \{cmd\}"\}', + dispatch_body, re.M)), + "兜底那句不在 dispatch 里了 —— 未知命令会返回 None") + + # 各个新方法里不能有那句兜底 + for meth in ("_run_update", "_do_update", "_do_mouse"): + mm = re.search(r"\n def %s\(self.*?(?=\n def |\Z)" % meth, + src, re.S) + body = mm.group(0) if mm else "" + check("%s 里没有引用 cmd" % meth, + "未知命令: {cmd}" not in body, + "兜底那句漏进 %s 了(会 NameError)" % meth) + + print() + print("=" * 58) + if FAILS: + print(" 失败 %d 项:%s" % (len(FAILS), ", ".join(FAILS))) + return 1 + print(" 全部通过") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/_tests/verify_all.ps1 b/_tests/verify_all.ps1 index 4931573..472a6ea 100644 --- a/_tests/verify_all.ps1 +++ b/_tests/verify_all.ps1 @@ -502,6 +502,29 @@ $stageOut = & python (Join-Path $root "_build\check_win7_compat.py") 2>&1 Show-Fails $stageOut Record "Windows 7 兼容" ($LASTEXITCODE -eq 0) +# ---------------------------------------------------------------- 3a10 +Banner "3a10/7 静默自动更新链路" +# 用户要求:"手表端点击一个更新按钮,电脑端就可以自动进行静默更新, +# 其他什么设置都不变 …… 不弹任何窗口出来" +# 这条链是:中转 /v1/update/latest -> 被控端 updater.py 下载校验 +# -> 分离 .cmd 帮手 -> 静默装 -> 重启。 +# +# 这里验协议常量、版本比较(按数字段,不是字符串)、下载校验、 +# 以及**服务端 dispatch 的结构没被补丁弄坏**。 +$stageOut = & python (Join-Path $root "_tests\test_update_flow.py") 2>&1 + Show-Fails $stageOut +Record "静默自动更新" ($LASTEXITCODE -eq 0) + +# ---------------------------------------------------------------- 3a11 +Banner "3a11/7 远程鼠标 + 一键显示桌面" +# 鼠标注入是最"有力量"的功能,参数校验写错的话表现是"手表点了没反应", +# 教室里没法排查。这里盯坐标归一化(除以 宽-1 那条规则)、 +# 滚轮夹取、Win+D 的事件顺序、以及非法参数必须被拒。 +$stageOut = & python (Join-Path $root "_tests\test_mouse_control.py") 2>&1 + Show-Fails $stageOut +Record "远程鼠标控制" ($LASTEXITCODE -eq 0) + + # ---------------------------------------------------------------- 3a5 Banner "3a5/7 局域网/中转对比基准(防测试自己骗人)" # 这个基准一度是红的,而且报告里写着「这是中转链路的产品 bug」—— diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml index 474e7ba..9dd228c 100644 --- a/android/AndroidManifest.xml +++ b/android/AndroidManifest.xml @@ -2,7 +2,7 @@ + android:versionName="2.1.6">