Files
Hearth/docs/devlog-2026-08-16.md
T
2026-08-16 17:50:28 +08:00

74 lines
5.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Hearth 启动器 · 开发日志
> 项目:HearthAndroid 启动器)· 包名 `top.yeij.hearth`
> 日期:2026-08-16
## 1. 项目概述
面向瑞芯微 RK3566 横屏开发板(立创·泰山派)的 Android 启动器(Home)。
核心架构:**界面全 H5 渲染,原生只当「能力层」**,通过 JS Bridge`window.HearthBridge`)双向调用,桌面 UI、卡片、webAPP 均可远程下发、动态扩展,无需重装 APK。
目标平台:Android 11API 30)· 横屏 800×480 · 高 DPI(自适应系统 density)· `minSdk=30` 不做低版本兼容。
## 2. 环境搭建(arm64 工具链,本次最大障碍)
本机是 aarch64 容器,而 Android SDK 的构建工具(aapt2 等)官方只发布 x86_64 Linux 版。搭建过程:
| 组件 | 方案 | 来源 |
|---|---|---|
| JDK 17 | apt | 清华源 |
| Gradle 8.9 | 二进制包 | 腾讯镜像 |
| Android SDK platform-35 + build-tools 34.0.0 | 手动下载 | 腾讯镜像(dl.google.com 不通,且镜像无 android-34 正式版,故 `compileSdk=35` |
| **arm64 aapt2/zipalign/split-select** | drop-in 替换 | Commit451/android-arm-build-toolsgh-proxy.com 加速) |
| 依赖仓库 | 阿里云 google/central 镜像 | `maven.aliyun.com` |
**关键结论**:官方 Google Maven 无 arm64 Linux 版 aapt2Issue #227219818),只能社区构建。通过 `android.aapt2FromMavenOverride` 指向 arm64 aapt2 绕开 Google Maven 的 x86_64 下载。
另:AGP 首次构建会卡死在 `dl.google.com` 的 SDK 自动下载(SYN_SENT 挂起),需 `android.builder.sdkDownload=false` 关闭。
## 3. 设计决策
- **全 H5 渲染**:桌面 = 全屏 WebView + H5,原生只提供查应用/启动/媒体状态/下载缓存等能力。
- **UI 风格 Miuix / HyperOS 4 视觉**(纯 CSS 还原,非 Compose 库):
- **壁纸透明**Activity `showWallpaper` + WebView `setBackgroundColor(TRANSPARENT)` + H5 `html/body` 透明。
- **柔光玻璃**(HyperOS 4 设计):卡片用 `backdrop-filter: blur(20px) saturate(1.2) brightness(1.05) contrast(1.1)` + 半透明背景 + 1px 描边(参数参考 Miuix `miuix-blur`saturation 1.2 / brightness +0.05 / contrast 1.1)。
- **卡片插件化**:每个卡片 = 独立 HTML + manifest(声明数据源/权限),可远程下发。
- **webAPP 多标签**:每个标签 = 一个独立 WebView(`Map<String, WebView>`),内容 WebView topMargin 44dp 露出 H5 顶栏。
- **媒体卡权限方案**`MEDIA_CONTENT_CONTROL` 是 signature|privileged 权限普通 APK 拿不到,改用 **NotificationListenerService**(用户授权"通知使用权"),`getActiveSessions(ComponentName)` 传 listener 组件作为授权凭据。
- **编译环境适配**`compileSdk=35` + AGP `8.6.1`(匹配 arm64 aapt2 8.6.x)。
## 4. 实现过程(14 个任务,TDD + 逐任务 review
| 阶段 | 任务 |
|---|---|
| 原生骨架 | 项目骨架+窗口管理 → WebViewManager+JsBridge → AppRepository |
| 桌面+列表 | H5 桌面壳 → 安卓APP列表 → CacheManager+WebApp清单 → H5应用列表 |
| 多标签 | WebAppContainer(标签状态数据层)→ 顶栏 H5 + tabs 纯函数 |
| 卡片系统 | CardRepository+首页三栏 → MediaSessionSource+媒体卡 |
| 收尾 | 设置页 → 集成收尾(接线+NotificationListenerService)→ 补充 webAPP 多标签 JS Bridge |
测试:Kotlin 单测 32 个(AppRepository/JsBridge/WebAppRepository/WebAppContainer/CacheManager 等)+ H5 node:test 8 个(cards.js 布局降级 / tabs.js 标签状态),全绿。
## 5. 关键问题与解决
1. **aapt2 arm64**:官方无 → Commit451 社区构建 drop-in 替换(见 §2)。
2. **JS 桥线程 vs 主线程**CriticalTask 14 review 发现):`@JavascriptInterface` 方法在 JavaBridge 后台线程运行,直接操作 View 会抛 `CalledFromWrongThreadException`。解决:所有 View 操作 `postToMainThread`(注入 `desktopWebView.post`marshal 到主线程。
3. **跨线程竞态**(最终 review 发现):WebAppContainer 的 `open/close/switchTo` 在主线程写、`listTabs` 在桥线程读同一 `mutableListOf`。解决:状态方法全部 `@Synchronized`
4. **innerHTML XSS**applist/webapplist/topbar 用 innerHTML 拼用户数据,桌面 WebView 持有桥权限可被注入。解决:全部改 `createElement` + `textContent`/`dataset`
5. **首页空卡片**`fetchCards` 在仓库未接线时返回 `"[]"``layout([])` 渲染空。解决:home.js 兜底补内置 time 卡。
6. **媒体卡权限**:见 §3NotificationListenerService 方案。
## 6. 遗留项(deferred,不影响 merge,后续处理)
- 真机验证:无 adb 设备,壁纸/柔光玻璃/媒体卡/多标签导航均未真机冒烟。
- `drawableToBase64` 未 recycle bitmap`timeTicker` setInterval 未清理。
- 沉浸模式仅在 onCreate 设置,返回前台未重设(建议 onResume)。
- 内容 WebView 无 onPause/onResume 生命周期同步。
- 设置页 Switch/Slider/版本号静态,交互未接。
- webAPP 服务器地址、卡片目录地址为占位常量,需真机配置。
- 二期:沉浸首页 freeform 小窗(需固件 `enable_freeform_support`)。
## 7. 分支与提交
开发走 `dev` 分支(`main` 为发布分支)。设计文档 `docs/superpowers/specs/`、实现计划 `docs/superpowers/plans/`、本日志 `docs/`