Files
lumaplayer/PROJECT_HANDOFF.md
beyond 91366f24b8 1、修复进度条无法拖动问题,
2、修复长按快进问题
3、增加亮度、音量调节
2026-08-28 21:33:58 +08:00

239 lines
14 KiB
Markdown
Raw Permalink 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.
# LumaPlayer 项目交接说明(移交下一 AI 开发者)
> 本文档是给下一位接手本项目的 AI 开发者/工程师的交接说明,请在动手前完整阅读。
> 交接时间:2026-08-28 | 工作目录:`F:\lumaplayerAPP\lumaplayer` | Git 分支:`master`
---
## 1. 项目是什么
**LumaPlayer** 是一款 Android(Kotlin + Jetpack Compose)的 **Emby 媒体库客户端**:
- 用户可添加 / 管理多个 Emby 服务器(支持 HTTP / HTTPS、登录认证、API Key)。
- 浏览媒体库(电影 / 剧集)、查看媒体详情、在线流媒体播放(Media3 ExoPlayer,支持 HLS)。
- 支持搜索、收藏、播放记录、图标库管理、主题 / 语言 / 玻璃风格设置等。
- UI 主打「液态玻璃 / 毛玻璃」设计风格,采用 Kyant0 Backdrop + 自定义降级玻璃。
目前处于**可构建、可运行**状态(Debug / Lint / Unit 通过,API 35 模拟器已回归),但**仍有未提交的工作区改动**(见第 6 节)。
---
## 2. 技术栈与版本
| 项 | 值 |
| --- | --- |
| 语言 | Kotlin + 少量 Java(Room 实体 / DAO) |
| UI | Jetpack Compose(Material 3) |
| 视频播放 | AndroidX Media3 ExoPlayer 1.5.1(+ HLS) |
| 网络 | Retrofit 2.11.0 + OkHttp 4.12.0 + Gson |
| 本地存储 | Room 2.8.4(数据库名 `emby_player.db`,当前 version 6) |
| 导航 | Navigation Compose 2.10.0 |
| 图片 | Coil 2.7.0 + Palette |
| 玻璃 / 液态 | `io.github.kyant0:backdrop:2.0.1`、`io.github.kyant0:shapes:1.2.1`、`top.yukonga.miuix.kmp:miuix-ui:0.9.4-rc01` |
| Gradle | 9.5.0(wrapper) |
| AGP | `com.android.application` 9.0.1 |
| Kotlin | 2.3.20(`org.jetbrains.kotlin.plugin.compose`) |
| compileSdk / targetSdk / minSdk | 35 / 35 / 24 |
| Java 兼容 | VERSION_21 |
> 仓库镜像源:`settings.gradle.kts` 使用阿里云镜像 + google() + mavenCentral();Gradle 发行版走腾讯云镜像(`gradle-wrapper.properties`)。
---
## 3. 目录结构
```
lumaplayer/
├── settings.gradle.kts / build.gradle.kts / gradle.properties / local.properties
├── gradle/
│ ├── libs.versions.toml # 版本目录(当前只有少量条目)
│ ├── gradle-daemon-jvm.properties
│ └── wrapper/ # Gradle 9.5.0 wrapper(腾讯镜像)
├── memory/ # 项目记忆库(重要!见第 7 节)
│ ├── MEMORY.md # 记忆索引
│ ├── projects.md # 项目状态 / 变更历史
│ ├── 2026-08-2x.md # 每日变更记录
│ ├── lessons.md # 踩坑教训(必读)
│ └── infra.md # 基础设施速查
└── app/src/main/
├── AndroidManifest.xml
├── java/com/example/myapplication/data/ # Room 实体/DAO(Java)
│ ├── AppDatabase.java # DB 版本 6,含 1→6 迁移
│ ├── ServerEntity.java / ServerDao.java
│ └── IconLibraryEntity.java / IconLibraryDao.java
├── kotlin/com/example/myapplication/
│ ├── MainActivity.kt # 启动入口,初始化主题/语言/Repository
│ ├── MainApp.kt # 根 Composable,玻璃 Backdrop 源层 + 底栏 + NavHost
│ ├── data/ # Emby API 与业务数据层
│ │ ├── EmbyApiModels.kt / EmbyApiService.kt / EmbyRepository.kt
│ │ ├── EmbyServer.kt # EmbyServer 模型(fullUrl 拼装逻辑)
│ │ ├── ServerRepository.kt / IconLibrary.kt
│ ├── navigation/
│ │ ├── AppRoute.kt # 路由定义(sealed interface)
│ │ ├── AppNavHost.kt # 导航宿主 + 各目的地 Composable
│ │ └── NavigationTransitions.kt
│ ├── screens/ # 各页面
│ │ ├── HomeScreen.kt / LibraryScreen.kt / RecordsScreen.kt
│ │ ├── SearchScreen.kt / MediaDetailScreen.kt / VideoPlayerScreen.kt
│ │ ├── SettingsScreen.kt / SettingsChildScreens.kt
│ │ └── library/IconLibraryManagerScreen.kt
│ └── ui/
│ ├── AppTheme.kt # 主题 / 玻璃模式(Liquid/Frosted)全局状态与持久化
│ ├── AppLanguage.kt / AppBottomBar.kt / ImmersiveBackground.kt
│ ├── LiquidGlass.kt # 玻璃控件组件库(对话框、表单、菜单等)
│ ├── design/ # 设计系统
│ │ ├── LumaDesignSystem.kt # 颜色/令牌/GlassSpec/LumaGlassRuntime/rememberLumaLayerBackdrop
│ │ ├── LumaComponents.kt # 公共组件(加载/空态/按钮/分段等)
│ │ └── LumaIcons.kt
│ └── liquidglass/ # 液态底栏专有实现
│ ├── LiquidBottomTabs.kt / LiquidBottomTab.kt
│ └── DampedDragAnimation.kt / DragGestureInspector.kt / InteractiveHighlight.kt
└── res/
├── drawable/ # aurora_* 图标子集(大量 XML selector)
├── values/strings.xml(中文)、values-en/strings.xml(英文) # 双语
└── mipmap-*/ 图标
```
> 注意:应用包名/命名空间仍是默认的 `com.example.myapplication`,尚未改名为 `com.luma.player` 之类。是否改名属可选优化,改动会涉及 namespace、applicationId、Manifest、Room 相关路径。
---
## 4. 核心架构
### 4.1 数据层(`data/`)
- **Room 数据库 `emby_player.db`**:表 `servers` 与 `icon_libraries`。
- 版本 **6**,定义了 1→2、2→3、3→4、4→5、5→6 的迁移(见 `AppDatabase.java`)。
- `ServerEntity`(Java)字段:id、name、protocol、host、basePath、port、username、password、note、accessToken、userId、iconUrl、isSelected。
- protocol(http/https)是 5→6 迁移新增的**可空列**,模型中读取时兜底为 `https`。
- **Emby 业务层都由 `EmbyRepository`(object 单例)** 承载,主要能力:
- 认证:`AuthenticateByName`(用户名密码换 token)。
- 媒体库:`Library/MediaFolders`、`Users/{userId}/Items`、`Items/{itemId}/PlaybackInfo` 等。
- 图片 URL 拼装:`buildImageUrl` / `buildBackdropUrl` / `buildEpisodeImageUrl` / `buildPersonImageUrl`。
- 在线检测:`checkOnline`(`System/Info/Public`)。
- **播放进度回报**:内部用 `Channel` 串行队列 + 工作协程,把 开始 / 进度 / 停止 事件按 Emby Playback Check-in 约定上报;进度按 10 秒或位置变化阈值上报。
- `EmbyServer.fullUrl`:统一负责协议、端口、basePath 的拼接与 `http://`/`https://` 前缀去重。
### 4.2 导航(`navigation/`)
- `AppRoute`(sealed interface)定义路由:
- 根 Tab:`home`、`records`、`library`、`settings`、`search`。
- 子页:`detail/{serverId}/{itemId}`、`player/{serverId}/{itemId}/{seriesId}/{seasonId}`、`library/icons`、`settings/cache`、`settings/about`、`library/add-server`。
- `AppNavHost.kt` 承载各目的地 Composable;根 Tab 用 `popUpTo + launchSingleTop + saveState/restoreState` 切换。
- 细节页 / 播放器用 `launchSingleTop` 防止重复入栈。
- `rootTabIndex` / `isRootRoute` 判断当前是否根 Tab。
### 4.3 UI / 玻璃系统(重点,历史坑多)
- **玻璃模式**(`AppTheme.kt`):`GlassMode.Liquid`(液态)/ `GlassMode.Frosted`(毛玻璃),用户手动在设置页选择,写入 SharedPreferences `app_settings` 持久化。
- **运行时判定**(`LumaDesignSystem.kt` 中的 `LumaGlassRuntime`):
- `isBackdropSafe = SDK_INT >= S`(API 31+)。
- `useNativeBackdrop = glassMode == Liquid && isBackdropSafe`:API 31+ 且液态模式才走 Kyant `rememberLayerBackdrop`;否则返回 `null`(用渐变毛玻璃降级)。
- `rememberLumaLayerBackdrop()` 用 `key(AppTheme.glassMode)` 包裹,确保模式切换时刷新 Backdrop 与 GlassSpec 缓存。
- **关键渲染原则(务必遵守,否则会 RenderThread SIGSEGV 崩溃)**:
1. `layerBackdrop`(源层)与使用 `drawBackdrop` 的玻璃控件必须是**同一棵树下的兄弟层**,来源层只包底层内容(背景 / 视频 AndroidView),玻璃消费层在源层之外。
2. **禁止**在同一个 backdrop 捕获范围内再绘制使用同一 backdrop 的玻璃控件,会形成循环渲染 → RenderThread SIGSEGV。
3. **播放器不捕获 AndroidView**;页面捕获层只服务根 Tab / 底栏。
4. 弹出层 / 菜单必须用页面内部专属 source,不能直接消费包住整个页面的外层 source。
5. 降级玻璃的边缘高光要在 `drawContent()` 之前绘制,否则文字/图标会被高光洗白。
- **降级毛玻璃**:当 `useNativeBackdrop == false` 时,用 Compose 渐变 + tint/边框/圆角模拟玻璃(见 `LumaGlassLevel` / `GlassSpec`)。
- **设计系统**:`LumaColors`、`LumaShapes`、`LumaTypography`、`LumaMotion`、`LumaIcons` 等令牌;公共组件在 `LumaComponents.kt`(加载 / 空态 / 按钮 / 分段控件 / 输入框等)。
### 4.4 播放器(`screens/VideoPlayerScreen.kt`)
- 基于 Media3 ExoPlayer + `PlayerView`(AndroidView)。
- 支持:起播位置恢复、暂停/继续、进度拖拽、剧集/字幕菜单、全屏、播放进度回报(串行队列)。
- 当前工作区正在大改播放器 UI(见第 6 节)。
---
## 5. 构建与运行
```powershell
# 在项目根目录
.\gradlew.bat assembleDebug # Debug 构建
.\gradlew.bat lintDebug # 静态检查
.\gradlew.bat testDebugUnitTest # 单元测试
```
- Gradle 9.5.0 wrapper(腾讯镜像),AGP 9.0.1,Kotlin 2.3.20。
- SDK 路径在 `local.properties`(本机:`C:\Users\Administrator\AppData\Local\Android\Sdk`),该文件被 gitignore,不入库。
- `app/build.gradle.kts` 中有任务钩子:跳过 `AarMetadata` 相关 `check` 任务(`tasks.whenTaskAdded`);`gradle.properties` 设 `android.aarMetadataCheck.enable=false`、`android.suppressUnsupportedCompileSdk=37.0`。
- 历史提交常在 `assembleDebug`、`lintDebug`、`testDebugUnitTest` 三者通过后收尾。真机 GPU 稳定性尚未充分验证(主要是 RenderThread)。
> 注意:`memory/infra.md` 里写的 JDK 路径是 macOS 路径(`/Applications/Android Studio.app/...`),那是历史环境信息,与当前 Windows 工作环境不一致,仅作参考。
---
## 6. 当前工作区状态(⚠️ 交接重点)
Git 分支 `master`,与 `origin/master` 同步。**存在 3 个文件未提交**:
| 文件 | 改动 |
| --- | --- |
| `app/build.gradle.kts` | 重新加入 `androidx.compose.material:material-icons-extended` 依赖 |
| `navigation/AppNavHost.kt` | `onPlaybackProgress` 回调签名简化:由外部传 `eventName` 改为内部根据 `paused` 自动映射为 `"Pause"` / `"TimeUpdate"` |
| `screens/VideoPlayerScreen.kt` | **大范围重构**(+518/-502):播放器新增信号/电量/存储状态、连接状态监测、玻璃菜单、Material 图标等;改动与远端设计系统可能存在交互,**提交前务必重新编译 + 回归** |
> 最近 6 条提交:
> ```
> 07dc93c 收口玻璃交互与界面细节
> 8f59235 收敛玻璃边缘并清理图标库旧界面
> 594f47b 修复玻璃模式切换并统一设置组件
> dafac3e 取消液态玻璃安全门并收敛底栏绘制
> 485538f 收敛降级玻璃与页面状态恢复
> e76637c 完善降级玻璃界面并增加手动玻璃模式
> ```
**接手建议**:先 `git diff` 复核上述未提交改动是否完整、可编译,确认无误后再提交/推送;若与远端有冲突,按 `memory/lessons.md` 的三方合并经验处理(先暂存本地改动再合并,避免整文件覆盖)。
---
## 7. 项目记忆库(必读)
`memory/` 目录记录了完整开发历史与踩坑经验,**接手前建议通读**:
- `MEMORY.md` — 项目记忆索引、当前实现概览、稳定性策略。
- `projects.md` — 逐日项目状态与变更历史。
- `2026-08-26.md` / `2026-08-27.md` / `2026-08-28.md` — 每日详细变更与教训。
- `lessons.md` — **踩坑教训集(重点)**,尤其是 RenderThread SIGSEGV 的玻璃渲染约束、Room 迁移可空性、Emby 播放回报、导航状态等。
- `infra.md` — Git 远端地址等。
---
## 8. 已知要点 / 注意事项(给下一位开发)
### Git
- 远端:`https://git.leee.xin/beyond/lumaplayer.git`(origin,分支 master)。认证信息不要写入远端配置。
- 本地 `memory/` 保留本地参考素材(未跟踪,不随源码提交),删除前务必确认无引用。
- 清理遗留实现前,一律用全仓库引用检索确认目标未被活动页面使用。
### 玻璃渲染(崩溃风险最高)
- 任何修改涉及 `layerBackdrop` / `drawBackdrop` / 弹窗 / 菜单 / 播放器,都要遵守第 4.3 节原则,否则可能 RenderThread SIGSEGV。
- 玻璃模式是全局 Compose 状态,涉及 Backdrop / GlassSpec 缓存时必须把 `AppTheme.glassMode` 纳入 `remember` 的 key。
- 模拟器不一定用标准指纹,`isBackdropSafe` 已简化为只看 API 级别;真机 GPU 的液态玻璃稳定性仍需单独验证。
### Room
- 改 `ServerEntity` / 新增列时,迁移列定义必须与 Room 的 `notNull` 预期一致(参考 `protocol` 可空列的教训),否则旧库启动会崩。
### 播放回报
- 必须沿用 PlaybackInfo 返回的 `PlaySessionId` / `MediaSourceId`,开始/进度/停止事件串行化;回报协程不要绑到播放器 Composable 生命周期(返回页面会丢停止事件)。
### 无障碍 / 触控
- `BasicTextField` 的 `decorationBox` 无论是否为空都必须调用 `inner()`,否则破坏焦点/光标。
- 交互目标尽量 ≥ 48dp 触控范围。
---
## 9. 可能的后续方向(非必须,供参考)
若产品方向需要,可考虑:
- 包名/命名空间从 `com.example.myapplication` 规范命名。
- 真机 GPU 上逐一验证液态玻璃(RenderThread 稳定性)。
- 补充更完整的单元测试覆盖(目前测试较少)。
- 是否支持 Jellyfin(API 兼容)等,属于需求决策,需与产品确认,勿自行扩展。
---
*本文档基于 `memory/`、源码与 Git 状态整理生成,供下一位 AI 开发者快速上手。动手前请先确认第 6 节未提交改动。*