# Android 客户端

星线 Android 使用 Kotlin 与 Jetpack Compose 实现聊天、工作台、记忆和个人设置。当前工程版本标记为预览版；本文说明源码组织、构建配置、数据流和平台能力边界。

## 工具链声明

| 配置 | 当前源码值 |
| --- | --- |
| applicationId / namespace | `cn.thestarline.workbench` |
| 最低系统 | Android 8.0，API 26 |
| compileSdk / targetSdk | 35 / 35 |
| Java / Kotlin JVM target | 17 / 17 |
| Kotlin 与 Compose 编译插件 | 2.0.21 |
| Android Gradle Plugin | 8.7.3 |
| Gradle Wrapper | 9.2.1 |
| versionName / versionCode | `1.0.6-preview` / `7` |

这些是仓库配置声明。本轮没有执行构建，Gradle 与 AGP 的兼容性、开发机 JDK 和最终产物版本需在发布环境核对；不能仅凭配置表认定工具链已验证。Compose BOM 声明为 `2024.12.01`，通话使用 LiveKit Android `2.29.0`，扫码使用打包式 ML Kit、CameraX 与 ZXing。

## 入口与页面状态

`MainActivity` 创建 `AppState`，接收初始 Intent 和后续深链，再通过 `StarlineApp` 组合页面。`AppState` 集中维护当前 tab、page、会话、聊天列表、新事、任务、收藏和记忆状态，使用 Compose 可观察状态与协程更新界面。

页面由 `tab:page` 状态映射到聊天、账号、任务、浏览器、记忆编辑、转出和扫码等 Composable。主布局处理状态栏、导航栏与输入法 inset，内容宽度上限为 720dp；底部导航按当前页面显示。页面切换和新事卡片过渡会读取系统动画开关。Activity 销毁时调用状态清理，停止所属任务和资源。

修改现有功能时应沿用 `AppState` 的操作入口和页面状态，避免在独立 Composable 中再建一套会话或持久化状态。新数据模型应明确字段兼容规则；当前大量服务对象使用 `JSONObject`，新增字段应防止缺失值导致崩溃。

## 网络与认证

`ServiceApi` 在 IO 协程中使用 `HttpURLConnection`，负责 JSON 请求、媒体上传、受大小限制的下载以及服务端错误转换。普通请求、事件轮询和媒体操作使用不同超时。`AppState` 的事件循环调用 `/events` 并刷新当前会话与页面数据。

`starlineChatBaseUrl` Gradle 属性生成 `BuildConfig.CHAT_BASE_URL`，默认指向 `https://chat.thestarline.cn`。Release 清单禁止明文流量，Debug 清单单独允许本地 HTTP 联调。即使构建属性接受 HTTP，正式包仍受清单限制；测试服务和账号服务应使用同一环境。

登录使用本机 PKCE 事务，打开 Account 返回的验证页面，经 `starlineworkbench://chat-auth` 回到 App。待处理事务和会话使用本机加密文件保存。配置与安全约束见[认证](https://document.thestarline.cn/app/authentication.md)。

## 本机数据与系统能力

`LocalData` 将任务、收藏、最近访问和偏好保存在私有 SharedPreferences；会话、记忆索引和附件经过 `SecureFile` 加密写入私有文件。密钥由 Android Keystore 生成，算法为 256 位 AES-GCM，写入先落临时文件并同步后重命名。清单设置 `allowBackup=false`。任务和普通偏好并未因此变成 AES-GCM 加密数据，数据分类见[安全与隐私](https://document.thestarline.cn/app/security-and-privacy.md)。

扫码相机由 CameraX 绑定生命周期，ML Kit 在本机解析二维码；也可选择图片或手动输入。实时位置通过 `LiveLocationService` 前台服务和持续通知维持，城市贴纸识别采用另一条短时定位路径。文件预览使用私有 FileProvider 与临时 URI 授权。权限细节见[权限与可访问性](https://document.thestarline.cn/app/permissions-accessibility.md)。

## 与 iOS 的具体差异

| 范围 | Android 当前行为 |
| --- | --- |
| 个人任务 | 待办／已完成／全部，无独立任务搜索与逾期筛选；日期选到当天末尾 |
| 首页今日待办 | 包含正数到期且不晚于今天末尾的未完成项，实际也包含历史逾期 |
| 网站去重 | 比较完整 URL 字符串，不复用 iOS 去 fragment、默认端口的规范化 |
| 记忆主题 | 固定模板和关键词；不调用 iOS FoundationModels |
| 原生 pending | 保存 verifier/state/到期时间，不持久化 API 基址或换票 ticket |
| 授权来源 | 当前仅信任正式 Account HTTPS，覆盖聊天地址不会自动放行本地 Account |
| 普通发送重试 | 未确认 clientId 保存在进程内映射，按账号、聊天范围和具体提交内容绑定 |
| 记忆转出 | 授权后先选目标 URI，再本机生成文件；无通用导入入口 |

这些差异需要在调用方和测试预期中体现，不能用 iOS 的页面、持久化策略或测试结果代替 Android 验收。任务和浏览器实现见[工作台](https://document.thestarline.cn/app/workbench.md)，存储字段与兼容见[本机数据格式](https://document.thestarline.cn/app/local-data-formats.md)。

`MainActivity` 销毁会 dispose 当前 AppState、停止所属协程并释放通话资源；持续位置共享另由前台服务管理。切到后台与 Activity 销毁不同，关闭聊天页也不自动结束位置或通话。普通事件轮询不是后台通知服务，短时重连、进程恢复和锁屏来电需分别验证，见[客户端生命周期](https://document.thestarline.cn/app/client-lifecycle.md)。

## 开发与验收

在 `android-app` 目录可执行：

```sh
./gradlew :app:testDebugUnitTest :app:assembleDebug
```

设备测试放在 `app/src/androidTest`，本地单元测试放在 `app/src/test`。本轮只审阅源码，没有执行以上命令或安装 APK。已有测试文件不表示当前提交已经通过。

Android 预览客户端与 iOS 并非所有能力对等：iOS 系统模型主题生成没有移植，Android 使用本机关键词推荐；Rust OpenMLS 加密聊天尚未接入。后台来电推送、真机跨网络音视频、真实相机与完整多因素转出应分别验收，不能由模拟器页面或 `active` 信令状态推断完成。参见[本地记忆](https://document.thestarline.cn/app/local-memory.md)、[消息与媒体](https://document.thestarline.cn/app/messaging-and-media.md)、[测试](https://document.thestarline.cn/app/testing.md)和[发布](https://document.thestarline.cn/app/releases.md)。
