# GSYVideoPlayer Compose 能力 Backlog 与推进 Plan > 本文件归档 [v13.0.0 ~ master `46ef8db3`] 三路并行审核(破坏性 / 能力对齐 / Demo 对齐)的全部结论,并把后续工作拆成可逐轮推进、可勾选完成的子任务。 > > **Compose 模块当前状态:未发布(Unreleased)**。所有动作均不打 tag,随 master 滚动迭代;首发将以本文件 P4-1 全部完成 + P5-1 至少一半完成 为基线再视情况评估。 > > 校对时间:2026-05-20(J 轮 Java 端基础能力真机回归)。基线 commit:[`9cafdb08`](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer)。 --- ## 0. 全局视图 | 维度 | 现状 | 目标 | |---|---|---| | 是否破坏既有功能 | ✅ 0 破坏 | 持续保持(每轮 `:app:assembleDebug` + `:app:assembleRelease` + 真机 monkey + crash buffer 空) | | Wrapper 模式 vs Java | ✅ 几乎完全等价 | 维持,仅做小修补(autoPauseResume 已默认开) | | Native 模式 vs Java | ⚠️ "最小子集 + 逃生口堵死" | P4-1 → P4-2 推进至"等价" | | Demo 覆盖 | 8/41 ≈ 19.5%,**13 类差异化能力空白** | P5-1 必补 8 个 → P5-2 选补 8 个 → 总覆盖 ≈ 24/41 ≈ 58%(**当前进度:24/41 ≈ 58%,P5-1 8/8 已完成;P5-2 8/8 已完成(D9/D10/D11/D14 上半场 + D12/D13/D15/D16 下半场),首发基线达成**) | | 文档与发布约束 | doc/COMPOSE_USE.md 已写明"未发布" | 持续与代码同步,每轮回归此文件 | --- ## 1. 缺失/异常能力清单(按风险等级) ### 1.1 🔴 P0 | 代码核心残缺(必修,且可能影响首发) > ✅ **R2 已全部修复**(详见 § 3 进度跟踪表);下表保留作为历史可追溯的"问题→定位→验收→实际落点"记录。 | ID | 问题 | 现状定位 | 验收标准 | R2 落点 | |---|---|---|---|---| | P0-1 ✅ | `GSYPlayerController.host` 是 `internal`,Native 模式所有"Standard 已有但 Controller 未暴露"的能力(字幕 / 滤镜 / 镜像 / 列表小窗 / setSeekOnStart / 快照截图 / GIF)无法通过逃生口访问 | [GSYPlayerController.kt#L23-L24](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/gsyVideoPlayer-compose/src/main/java/com/shuyu/gsyvideoplayer/compose/native_/GSYPlayerController.kt#L23-L24) | 提供 `controller.withHost { player -> ... }` 闭包(限定主线程 + 自动 null-check + released 期间 no-op);新增单测/Demo 覆盖一次"调 setSubtitleSources" 路径 | ✅ Controller 新增 `withHost(block)`:主线程检查(非主线程抛 IllegalStateException)+ released no-op 返回 null;KDoc 显式禁止在 block 里调 `setVideoAllCallBack`;`host: internal var` 保留不破坏二进制 | | P0-2 ✅ | `installInternalCallback` 直接 `player.setVideoAllCallBack(...)`,覆盖用户 callback;用户用 `withHost` 调 `setVideoAllCallBack` 时也会反向覆盖内部 callback,事件流断裂 | [GSYPlayerController.kt#L165-L187](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/gsyVideoPlayer-compose/src/main/java/com/shuyu/gsyvideoplayer/compose/native_/GSYPlayerController.kt#L165-L187) | 改为"内部 callback 链式分发 + 保留用户 setUserVideoAllCallBack 注入点";单测:用户 callback 与 events 同时触发时双方都收到 | ✅ 重构为稳定 dispatcher 单例(`internalDispatcher`,22 项全覆盖)+ `@Volatile userCallback` 字段;新增 `setUserVideoAllCallBack(callback)` 公开入口;克隆全屏时 dispatcher 引用本身被 Java 侧 `setVideoAllCallBack` 复制,事件流跨克隆体不断 | | P0-3 ✅ | Native 模式没有全屏切换 API:`GSYPlayerController` 缺 `enterFullscreen / exitFullscreen`,`GSYPlayerEvent` 缺 `EnterFull / QuitFull` | [GSYPlayerEvent.kt](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/gsyVideoPlayer-compose/src/main/java/com/shuyu/gsyvideoplayer/compose/native_/GSYPlayerEvent.kt) [GSYPlayerController.kt](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/gsyVideoPlayer-compose/src/main/java/com/shuyu/gsyvideoplayer/compose/native_/GSYPlayerController.kt) | Controller 新增 `enterFullscreen(activity, hideActionBar=true, hideStatusBar=true)` / `exitFullscreen(activity)`;`GSYPlayerEvent` 加 `EnterFull` / `QuitFull`;Demo `FullFeatureNativeActivity` 接入按钮真机回归 ≤ 1.5s 切回 | ✅ Controller 新增 `enterFullscreen(activity, hideActionBar, hideStatusBar)` / `exitFullscreen(activity)` / `isFullscreen`;`GSYPlayerEvent` 加 `EnterFull` / `QuitFull` data object;ΔD2/ΔD8 接入;额外修复 [GSYComposeHostPlayer.java](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/gsyVideoPlayer-compose/src/main/java/com/shuyu/gsyvideoplayer/compose/native_/GSYComposeHostPlayer.java) class+构造器 → public(GSY 反射克隆要求 public Constructor,否则 NoSuchMethodException);emulator 回归:双 host 树确认(原 host + `app:id/full_id` 克隆体),BACK 退出 OK,二轮 round trip 0 crash | ### 1.2 🟡 P1 | 代码次重要残缺 | ID | 问题 | 现状定位 | 验收标准 | |---|---|---|---| | P1-1 | Native 模式手势完全禁用且无 Compose 替代品:vol/brightness/seek 三 flag 强制 false,未提供 `Modifier.gsyGestureControl(controller)`;锁屏退化为单纯布尔标志 | [GSYComposeHostPlayer.java#L99-L105](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/gsyVideoPlayer-compose/src/main/java/com/shuyu/gsyvideoplayer/compose/native_/GSYComposeHostPlayer.java#L99-L105) | 新文件 `GSYGestureModifier.kt` 提供 `Modifier.gsyGestureControl(controller, enableSeek/Volume/Brightness, onCenterToast)`;锁屏激活时手势屏蔽生效;`FullFeatureNativeActivity` Demo 集成 | | P1-2 | `rememberGSYPlayerController` 只 release,不自动 pause/resume;后台切换不暂停,与 Wrapper 行为不一致 | [GSYPlayerController.kt#L1-L60](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/gsyVideoPlayer-compose/src/main/java/com/shuyu/gsyvideoplayer/compose/native_/GSYPlayerController.kt#L1-L60) [GSYPlayerSurface.kt](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/gsyVideoPlayer-compose/src/main/java/com/shuyu/gsyvideoplayer/compose/native_/GSYPlayerSurface.kt) | `rememberGSYPlayerController(autoPauseResume = true)` 默认开;ON_PAUSE → `GSYVideoManager.onPause()`;ON_RESUME → `GSYVideoManager.onResume()`;HOME → 切回真机回归无 crash | | P1-3 | `GSYPlayerEvent` 仅 3 类,`VideoAllCallBack` 22 项中 19 项未映射:缺 `Buffering(start/end/percent)` / `SeekComplete` / `EnterSmall` / `QuitSmall` / `ClickStart/Resume/Stop/Seekbar/Blank` / `TouchScreenSeek*` 等 | [GSYPlayerEvent.kt](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/gsyVideoPlayer-compose/src/main/java/com/shuyu/gsyvideoplayer/compose/native_/GSYPlayerEvent.kt) | sealed class 扩 19 项;`installInternalCallback` 全数 `tryEmit`;保持兼容(旧的 Prepared/AutoComplete/Error 不动) | | P1-4 | Native Snapshot 只读字段缺失:`videoSar(Num/Den)` / `netSpeed(Long/text)` / `isCacheReady` 全部没暴露 | [GSYPlayerSnapshot.kt](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/gsyVideoPlayer-compose/src/main/java/com/shuyu/gsyvideoplayer/compose/native_/GSYPlayerSnapshot.kt) | 扩字段 + `syncFromHost` 同步;Demo 加可视化 | | P1-5 | builder-only 字段没有 controller 直 setter:headers / cachePath / setSeekOnStart / setLooping / setStartAfterPrepared / setOverrideExtension / setShowPauseCover / setReleaseWhenLossAudio | [GSYPlayerController.kt](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/gsyVideoPlayer-compose/src/main/java/com/shuyu/gsyvideoplayer/compose/native_/GSYPlayerController.kt) | Controller 加直 setter,内部委托 host;保留 `setUp(builder)` 的兼容路径 | | P1-6 | 缓冲粒度差异:500ms 轮询会丢真实 `onBufferingUpdate(percent)` 抖动 | 同 P1-3 / [GSYPlayerController.kt#L34-L42](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/gsyVideoPlayer-compose/src/main/java/com/shuyu/gsyvideoplayer/compose/native_/GSYPlayerController.kt#L34-L42) | `events.BufferingProgress(percent)` 实时发;Snapshot 保留低频字段 | ### 1.3 🟢 P2 | 一致性 / 卫生(小问题,但影响首发口碑) > ✅ **R1 已全部修复**(详见 § 3 进度跟踪表);下表保留作为历史可追溯的"问题→定位→验收→实际落点"记录。 | ID | 问题 | 现状定位 | 验收标准 | R1 落点 | |---|---|---|---|---| | P2-1 | "9 个 Compose Demo" 措辞错误,实际只有 8 个(`DemoSamples.kt` 是 data object,不是 Activity) | [README.md#L156-L158](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/README.md#L156-L158) [README_CN.md#L159-L161](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/README_CN.md#L159-L161) [doc/COMPOSE_USE.md](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/doc/COMPOSE_USE.md) [ComposeDemoListActivity.kt](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/app/src/main/java/com/example/gsyvideoplayer/compose/ComposeDemoListActivity.kt) | 三处文档统一改为"8 个示例",并标明每个示例对应的 Java demo 来源 | ✅ 三处全改为 "8 个可运行 Compose Activity",并显式说明 `DemoSamples.kt` 是 `data object` | | P2-2 | 9 个 Compose Activity 全部 `exported="true"`,与老 Activity 默认 `false` 不一致 | [AndroidManifest.xml#L250-L294](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/app/src/main/AndroidManifest.xml#L250-L294) | 改 `exported="false"`;`adb am start -n` 调试需求改写到 README 用 monkey 替代说明 | ✅ 9 个 Activity 全改 `exported="false"`;emulator 验证:内部跳转 ✅ + 外部 `am start` 被严格拒绝(`SecurityException: not exported from uid 10211`) | | P2-3 | `consumer-rules.pro` 仅 7 行注释空规则;将来如有反射 / `@Composable` 类被业务做反射查找会被 R8 剥离 | [consumer-rules.pro](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/gsyVideoPlayer-compose/consumer-rules.pro) | 加最小兜底:`-dontwarn com.shuyu.gsyvideoplayer.compose.**` + 注释说明保留底线 | ✅ 加最小集 keep(HostPlayer / Controller / Snapshot / Surface + Event sealed 全部子类 + Wrapper Kt facade + LifecycleBridgeKt),`assembleRelease` R8 通过 | | P2-4 | `mediaVersion` 1.10.0→1.10.1 升级与 Compose 任务捆绑,未独立 commit | [gradle/dependencies.gradle#L5](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/gradle/dependencies.gradle#L5) | 文档备注(影响极小,不强制回滚);后续依赖升级单独 commit | ✅ 在 [gsyVideoPlayer-compose/build.gradle](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/gsyVideoPlayer-compose/build.gradle) 顶部加注释块,说明本模块不直接引用 media3、版本统一由 root `gradle/dependencies.gradle` 管理;后续仅需在根 `mediaVersion` 改一处即可 | | P2-5 | README 未明确"JDK 17"硬要求,新接入者用 JDK 8 会困惑 | [app/build.gradle#L30-L37](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/app/build.gradle#L30-L37) | README 顶部"环境要求"加一行 JDK ≥ 17 | ✅ 在 README.md / README_CN.md 的 Compose 章节"未发布"提示块下追加 🛠 工具链说明:CI=JDK 21 / 本地 JDK 17(模块 jvmTarget=17,要求 ≥ 17) | | P2-6 | PAT fallback 仍是有效 token 明文 | [build.gradle#L36-L42](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/build.gradle#L36-L42) | 提交 fallback 改空字符串(同时由仓主在 GitHub 撤旧 token + 新发;文档同步说明) | ✅ 状态核实:[release.yml](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/.github/workflows/release.yml) 已使用 `secrets.GITHUB_TOKEN`(CI 内置),未硬编码 PAT、无 fallback;`build.gradle` 取 token 路径在前轮 P3-5 已改为 properties/env 优先;本项无需再改文件 | ### 1.4 🟢 P3 | Demo 覆盖度(按 P5-1 / P5-2 / P5-3 三档分轮补) #### P5-1 | 必补的 8 个 demo(覆盖 GSY 招牌差异化能力) | ID | 待补 Demo | 对位 Java demo | 关键演示能力 | |---|---|---|---| | D1 | `DetailFilterComposeActivity` | [DetailFilterActivity](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/app/src/main/java/com/example/gsyvideoplayer/DetailFilterActivity.java) | GLSurface 滤镜 / 镜像 / 自定义 Render | | D2 | `CacheDownloadComposeActivity` | [DetailDownloadPlayer](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/app/src/main/java/com/example/gsyvideoplayer/DetailDownloadPlayer.java) + [DetailDownloadExoPlayer](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/app/src/main/java/com/example/gsyvideoplayer/DetailDownloadExoPlayer.java) + change_cache/clear_cache 按钮 | 缓存进度 + 清缓存 + Proxy↔EXO 切换 | | D3 | `AdInListComposeActivity` | [ListADVideoActivity2](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/app/src/main/java/com/example/gsyvideoplayer/ListADVideoActivity2.java) + [DetailADPlayer](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/app/src/main/java/com/example/gsyvideoplayer/DetailADPlayer.java) | 列表内夹广告 + 前贴片 | | D4 | `SubtitleComposeActivity` | [SubtitleDetailPlayer](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/app/src/main/java/com/example/gsyvideoplayer/SubtitleDetailPlayer.java) + [GSYExoSubTitleDetailPlayer](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/app/src/main/java/com/example/gsyvideoplayer/GSYExoSubTitleDetailPlayer.java) | 通用字幕 + EXO 外挂字幕选轨 | | D5 | `DanmakuComposeActivity` | [DanmkuVideoActivity](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/app/src/main/java/com/example/gsyvideoplayer/DanmkuVideoActivity.java) | 弹幕叠加 | | D6 | `ExoSwitchSourceComposeActivity` | [DetailExoListPlayer](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/app/src/main/java/com/example/gsyvideoplayer/DetailExoListPlayer.java) + [ExoAdaptiveTrackActivity](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/app/src/main/java/com/example/gsyvideoplayer/ExoAdaptiveTrackActivity.java) | 自定义 EXO MediaSource + HLS/DASH 自适应 | | D7 | `MultiWindowParallelComposeActivity` | [ListMultiVideoActivity](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/app/src/main/java/com/example/gsyvideoplayer/ListMultiVideoActivity.java)(CustomManager 真并行) | 升级现有 MultiWindow 由"互斥激活"为"真·多 Manager 并行" | | D8 | `SwitchSeamlessComposeActivity` | [SwitchListVideoActivity](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/app/src/main/java/com/example/gsyvideoplayer/SwitchListVideoActivity.java) + `SwitchUtil` | 列表 → 详情共享 surface 不重拉流 | #### P5-2 | 选补的 8 个 demo(现代 App 高频形态) | ID | 待补 Demo | 对位 Java demo | |---|---|---| | D9 | `VerticalShortVideoComposeActivity` ✅ | [ViewPager2Activity](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/app/src/main/java/com/example/gsyvideoplayer/ViewPager2Activity.java) | | D10 | `FloatingWindowComposeActivity` ✅ | [WindowActivity](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/app/src/main/java/com/example/gsyvideoplayer/WindowActivity.java) | | D11 | `MoreTypeComposeActivity` ✅ | [DetailMoreTypeActivity](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/app/src/main/java/com/example/gsyvideoplayer/DetailMoreTypeActivity.java) | | D12 ✅ | `AudioOnlyComposeActivity` | [AudioDetailPlayer](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/app/src/main/java/com/example/gsyvideoplayer/AudioDetailPlayer.java) | | D13 ✅ | `LocalFileComposeActivity` | [InputUrlDetailActivity](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/app/src/main/java/com/example/gsyvideoplayer/InputUrlDetailActivity.java) 本地分支 | | D14 | `WebDetailComposeActivity` ✅ | [WebDetailActivity](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/app/src/main/java/com/example/gsyvideoplayer/WebDetailActivity.java) | | D15 ✅ | `MediaCodecComposeActivity` | [RecyclerView3Activity](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/app/src/main/java/com/example/gsyvideoplayer/RecyclerView3Activity.java) | | D16 ✅ | `CustomControlsThemeComposeActivity` | 参考 [LandLayoutVideo](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/app/src/main/java/com/example/gsyvideoplayer/video/LandLayoutVideo.java) / [SampleControlVideo](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/app/src/main/java/com/example/gsyvideoplayer/video/SampleControlVideo.java) | #### P5-3 | 长尾选补(演示价值高、对 Compose 端"出 demo 卖点"有意义) | ID | 待补 Demo | 对位 Java demo | |---|---|---| | D17 | `TransparentVideoComposeActivity` | [DetailTransparentActivity](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/app/src/main/java/com/example/gsyvideoplayer/DetailTransparentActivity.java) | | D18 | `KeepLastFrameComposeActivity` | [KeepLastFrameDemoActivity](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/app/src/main/java/com/example/gsyvideoplayer/KeepLastFrameDemoActivity.java) | | D19 | `SmartPickComposeActivity` | [PlayPickActivity](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/app/src/main/java/com/example/gsyvideoplayer/PlayPickActivity.java) | | D20 | `TVFocusComposeActivity` | [PlayTVActivity](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/app/src/main/java/com/example/gsyvideoplayer/PlayTVActivity.java) | | D21 | `SharedElementTransitionComposeActivity` | [PlayActivity](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/app/src/main/java/com/example/gsyvideoplayer/PlayActivity.java) | | D22 | `FragmentInteropComposeActivity` | [FragmentVideoActivity](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/app/src/main/java/com/example/gsyvideoplayer/FragmentVideoActivity.java) | #### 现有 8 个 Compose demo "演示深度浅" 的回归升级(P5-Δ) | ID | 已有 Demo | 浅在哪里 | 升级目标 | |---|---|---|---| | ΔD1 | [BasicWrapperActivity](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/app/src/main/java/com/example/gsyvideoplayer/compose/host/BasicWrapperActivity.kt) | builder 仅设了 5 项,缺 ~30+ 项配置示范 | 加 `setVideoAllCallBack` + `setSeekRatio` + `setShowPauseCover` 等 | | ΔD2 ✅ | [DetailNativeActivity](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/app/src/main/java/com/example/gsyvideoplayer/compose/host/DetailNativeActivity.kt) | 全屏只是 `requestedOrientation`,没用底层全屏管线 | ✅ R2 已切换为 `controller.enterFullscreen / exitFullscreen` + `events.EnterFull/QuitFull` 同步本地标志位;BackHandler(enabled=fullscreen) 拦 BACK | | ΔD3 | [FullFeatureNativeActivity](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/app/src/main/java/com/example/gsyvideoplayer/compose/host/FullFeatureNativeActivity.kt) | 缺音量/亮度/进度手势条 + 中央 toast | P1-1 完成后接入 `Modifier.gsyGestureControl` | | ΔD4 | [ListPlayNativeActivity](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/app/src/main/java/com/example/gsyvideoplayer/compose/host/ListPlayNativeActivity.kt) | 仅"滚出屏暂停",没有"释放占位/封面恢复" | 演示 `setThumbImageView` + 离屏 release | | ΔD5 | [SwitchUrlActivity](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/app/src/main/java/com/example/gsyvideoplayer/compose/host/SwitchUrlActivity.kt) | 名为"切换 URL",实为 setUp 重置;没演示真 `SwitchUtil` | D8 出来后此 demo 文档加"see also"指向 | | ΔD6 | [MultiWindowActivity](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/app/src/main/java/com/example/gsyvideoplayer/compose/host/MultiWindowActivity.kt) | 单例 GSYVideoManager 互斥,没用 CustomManager | D7 出来后此 demo 改名为"互斥版",新增"并行版"对照 | | ΔD7 | [AutoPlayListActivity](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/app/src/main/java/com/example/gsyvideoplayer/compose/host/AutoPlayListActivity.kt) | 用 `events.AutoComplete` setUp 下一段,丢 surface 接管 | 先以注释说明取舍,等 P0-1 后可演示更深路径 | | ΔD8 ✅ | [ListWithFullscreenActivity](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/app/src/main/java/com/example/gsyvideoplayer/compose/host/ListWithFullscreenActivity.kt) | 用 `BackHandler+Insets` 模拟,没用底层全屏管线 | ✅ R2 已删除 `WindowInsetsControllerCompat` / 手动 `requestedOrientation` / `FullscreenLayer` 自绘,改为 `controller.enterFullscreen` + `events.EnterFull/QuitFull`,全屏渲染由内核克隆体接管 | --- ## 2. 推进 Plan(分轮,每轮一个 PR / 一组 commit) > 推荐顺序:**P3 轻量** → **P4-1 P0** → **P4-2 P1** → **P5-1 必补 demo** → **P5-2 选补 demo** → **P5-Δ 老 demo 升级 + 文档收口** → 评估首发。 > > 每轮收尾必跑: > 1. `./gradlew :gsyVideoPlayer-compose:assembleRelease` > 2. `./gradlew :app:assembleDebug` > 3. `./gradlew :gsyVideoPlayer-compose:publishToMavenLocal`(默认 + `-PPUBLISH_TARGET=mavenCentral`) > 4. emulator-5554 真机回归(受影响 demo + crash buffer 空验证) > 5. doc/COMPOSE_USE.md + README.md/README_CN.md 同步 > 6. **不发 tag**,commit + push master ### 轮次 R1 — P3 轻量修复(预计 0.5 天) ✅ 已完成 - [x] P2-1 文档"9 → 8 demo" 三处统一(README.md / README_CN.md / doc/COMPOSE_USE.md) - [x] P2-2 9 个 compose Activity Manifest `exported="false"` - [x] P2-3 `consumer-rules.pro` 加最小兜底 - [x] P2-4 `mediaVersion` 备注(在 compose 模块 build.gradle 顶部加注释块) - [x] P2-5 README/README_CN 在 Compose 章节标注 JDK 17 / CI JDK 21 工具链差异 - [x] P2-6 PAT fallback 状态核实:release.yml 已用 secrets.GITHUB_TOKEN,无需改文件 - [x] 真机回归:emulator-5554 装机成功;Monkey 200 事件 0 crash;same-uid 内部多层跳转 ✅;shell 跨 uid `am start` 被严格拒绝(`SecurityException: not exported from uid 10211`) - [x] 构建回归:`./gradlew :app:assembleDebug :gsyVideoPlayer-compose:assembleRelease :gsyVideoPlayer-compose:publishToMavenLocal -x lint` —— **BUILD SUCCESSFUL in 17s** - [x] commit `compose: R1 housekeeping (P2-1~6 docs/manifest/consumer-rules)` 并 push(不发 tag) ### 轮次 R2 — P4-1 代码 P0(预计 1.5 天) ✅ 已完成 - [x] P0-1 `controller.withHost { player -> ... }` 公开闭包;保留 `host: internal` 不破坏二进制 - [x] P0-2 `installInternalCallback` 重构为内部分发器:`internalCallback` + `userCallback` 链式 - [x] P0-3 Controller `enterFullscreen / exitFullscreen` + `GSYPlayerEvent.EnterFull / QuitFull` - [x] ΔD2 / ΔD8:[DetailNativeActivity](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/app/src/main/java/com/example/gsyvideoplayer/compose/host/DetailNativeActivity.kt) + [ListWithFullscreenActivity](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/app/src/main/java/com/example/gsyvideoplayer/compose/host/ListWithFullscreenActivity.kt) 切换到新全屏 API 并真机回归 - [x] doc/COMPOSE_USE.md 加"逃生口 withHost { ... } 用法"章节 - [x] 额外修复:[GSYComposeHostPlayer](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/gsyVideoPlayer-compose/src/main/java/com/shuyu/gsyvideoplayer/compose/native_/GSYComposeHostPlayer.java) class+构造器 → public(GSY 反射克隆 `getConstructor(Class[])` 仅返回 public 构造器;非 public 时 emulator 报 NoSuchMethodException 全屏失败) - [x] 真机回归:emulator-5554 装机;Monkey 200 事件 0 crash;ΔD2 全屏进/退(双 host 树确认 `app:id/full_id` 克隆体出现/移除);ΔD2 二轮 round trip dispatcher 复用稳定;ΔD8 全屏进/退(首次 setUp + 等 host attach 后的第二次点击进入全屏,符合 Compose 异步重组语义);6 个其他 demo same-uid 启动;cross-uid `am start` 仍被严格拒绝;AndroidRuntime FATAL 与 crash buffer 全空 - [x] 构建回归:`./gradlew :app:assembleDebug :gsyVideoPlayer-compose:assembleRelease :gsyVideoPlayer-compose:publishToMavenLocal -x lint` —— **BUILD SUCCESSFUL in 29s** - [x] commit `compose: R2 P0 fixes (withHost + callback fanout + fullscreen)` 并 push(不发 tag) ### 轮次 R3 — P4-2 代码 P1(预计 1.5 天) - [x] P1-1 `Modifier.gsyGestureControl(controller, ...)` + 锁屏联动 ✅ 新文件 [GSYGestureModifier.kt](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/gsyVideoPlayer-compose/src/main/java/com/shuyu/gsyvideoplayer/compose/native_/GSYGestureModifier.kt):横向 drag = seek(onDragEnd commit),左半屏纵向 = 亮度,右半屏纵向 = 音量;snapshot.isLocked 时所有手势短路;ΔD3 接入并加中央 toast - [x] P1-2 `rememberGSYPlayerController(autoPauseResume = true)` 默认开 ✅ ON_PAUSE → `GSYVideoManager.onPause()`,ON_RESUME → `GSYVideoManager.onResume()` - [x] P1-3 `GSYPlayerEvent` 扩 19 项 + `installInternalCallback` 全数 emit ✅ sealed class 5→24 项;dispatcher 22 项 callback 各加 `_events.tryEmit` - [x] P1-4 Snapshot 扩 SAR / netSpeed / isCacheReady ✅ 5 字段(videoSarNum/Den / netSpeed/Long/Text / isCacheReady) + syncFromHost 同步 + runCatching 兜底 - [x] P1-5 Controller 直 setter ✅ 8 个 setter(headers / cachePath / setSeekOnStart / setLooping / setStartAfterPrepared / setOverrideExtension / setShowPauseCover / setReleaseWhenLossAudio)+ 缓存 reapply 机制(attach 新 host / 全屏克隆体后自动 reapply)+ 主线程门 - [x] P1-6 `events.BufferingProgress(percent)` + `SeekComplete` 实时 ✅ [GSYComposeHostPlayer.java](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/gsyVideoPlayer-compose/src/main/java/com/shuyu/gsyvideoplayer/compose/native_/GSYComposeHostPlayer.java) 加 `BufferingHook` / `SeekCompleteHook`;Controller 在 attachHost / setUp 安装、detachHost / release 清理 - [x] ΔD3 [FullFeatureNativeActivity](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/app/src/main/java/com/example/gsyvideoplayer/compose/host/FullFeatureNativeActivity.kt) 接入 gestureModifier ✅ 三向手势 + 中央 toast - [x] R3 真机回归 ✅ ΔD3 进入 + 横/纵 swipe + Activity 存活;ΔD2 全屏 round trip;Monkey 50 events `--pct-touch 60 --pct-motion 30` 0 FATAL;状态显示 `Playing | 01:02 / 1:32:27` ### 轮次 R4 — P5-1 必补 demo(每个独立 commit / 8 commits 或一次合并) - [x] D1 DetailFilterCompose ✅ [DetailFilterComposeActivity.kt](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/app/src/main/java/com/example/gsyvideoplayer/compose/host/DetailFilterComposeActivity.kt) — `controller.withHost { player.setEffectFilter(...) }` 注入 6 种 GLSL 滤镜(NoEffect / Gamma / 黑白 / 反色 / Sepia / 高斯模糊),`GSYVideoType.setRenderType(GLSURFACE)` 在 onCreate 设置、onDestroy 还原 - [x] D2 CacheDownloadCompose ✅ [CacheDownloadComposeActivity.kt](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/app/src/main/java/com/example/gsyvideoplayer/compose/host/CacheDownloadComposeActivity.kt) — `ProxyCacheManager.instance().newProxy(ctx).getProxyUrl(url)` 套缓存代理 + `setCacheWithPlay(true)`;展示 `snapshot.isCacheReady` / `bufferPercent` / `netSpeedText`;清缓存按钮调 `GSYVideoManager.instance().clearAllDefaultCache(ctx)` - [x] D3 AdInListCompose ✅ [AdInListComposeActivity.kt](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/app/src/main/java/com/example/gsyvideoplayer/compose/host/AdInListComposeActivity.kt) — 单 controller 通过 `events.AutoComplete` 边沿事件链 setUp 切正片,简化 Java 双 player 模式;emulator 实证 `阶段:广告播放中 → 正片播放中` - [x] D4 SubtitleCompose ✅ [SubtitleComposeActivity.kt](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/app/src/main/java/com/example/gsyvideoplayer/compose/host/SubtitleComposeActivity.kt) — `PlayerFactory.setPlayManager(IjkPlayerManager.class)` 切回 IJK;3 字幕源(SRT 本地 / VTT 本地 / SRT 网络);`controller.withHost` 注入 `setSubtitleSources / setSubtitleStyle / setSubtitleEnabled / selectSubtitle`;字号 16↔22 sp 切换 - [x] D5 DanmakuCompose ✅ [DanmakuComposeActivity.kt](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/app/src/main/java/com/example/gsyvideoplayer/compose/host/DanmakuComposeActivity.kt) — Compose Canvas + `rememberTextMeasurer` 自绘 12 条 3 轨弹幕,与 `controller.snapshot.currentPosition` 联动;不依赖 master.flame.danmaku;emulator 实证 `Playing 48s/5547s` 真起播 - [x] D6 ExoSwitchSourceCompose ✅ [ExoSwitchSourceComposeActivity.kt](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/app/src/main/java/com/example/gsyvideoplayer/compose/host/ExoSwitchSourceComposeActivity.kt) — `PlayerFactory.setPlayManager(Exo2PlayerManager.class)` 切 EXO 内核;4 源(MP4/HLS/GSY 默认/IMG_0382)+ 5 档倍速(0.75/1.0/1.25/1.5/2.0);emulator logcat `ExoPlayerImpl: Init [AndroidXMedia3/1.10.1]` 实证;切 HLS 后新 instance Init + onPrepared - [x] D7 MultiWindowParallelCompose(CustomManager)✅ [MultiWindowParallelComposeActivity.kt](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/app/src/main/java/com/example/gsyvideoplayer/compose/host/MultiWindowParallelComposeActivity.kt) — Wrapper 模式 × 3 包 [MultiSampleVideo](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/app/src/main/java/com/example/gsyvideoplayer/video/MultiSampleVideo.java),每个 view 独立 `setPlayTag + setPlayPosition` → 路由到独立 `CustomManager.getCustomManager(getKey())`;区别于现有 P1 [MultiWindowActivity](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/app/src/main/java/com/example/gsyvideoplayer/compose/host/MultiWindowActivity.kt)(互斥版);`onDispose` 释放 3 个 manager - [x] D8 SwitchSeamlessCompose(共享 surface)✅ [SwitchSeamlessComposeActivity.kt](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/app/src/main/java/com/example/gsyvideoplayer/compose/host/SwitchSeamlessComposeActivity.kt) — Compose 端 seamless 精髓:同一 controller 跨 list 缩略区与 detail 大屏区复用(`GSYPlayerSurface` attach/detach 切换位置),不重 setUp、不释放、进度连续 - [x] [ComposeDemoListActivity.kt](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/app/src/main/java/com/example/gsyvideoplayer/compose/ComposeDemoListActivity.kt) 8 → 12 → 16 项;8 个新 Activity 在 [AndroidManifest.xml](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/app/src/main/AndroidManifest.xml) 注册 `exported="false"` - [x] R4 真机回归 ✅ emulator-5554 装机;上半场 4 个 demo am start + BACK + Monkey 50 events 0 FATAL;下半场 4 个 demo 通过 UI Automator 真点击进入:D6 logcat `ExoPlayerImpl: Init` + `onPrepared` + 切 HLS Release/Init / D5 长视频 Playing 48s/5547s / D3 阶段切换 ad→feature 实证 / D7 onPrepared + CURRENT_STATE_PLAYING;Monkey 100 events 0 FATAL;logcat AndroidRuntime FATAL/crash buffer 全空 ### 轮次 R5 — P5-2 选补 demo(视进度执行) - [x] D9 VerticalShortVideoCompose ✅ [VerticalShortVideoComposeActivity.kt](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/app/src/main/java/com/example/gsyvideoplayer/compose/host/VerticalShortVideoComposeActivity.kt) — Compose Foundation `VerticalPager + rememberPagerState`,单 controller 通过 `snapshotFlow { pagerState.currentPage }.distinctUntilChanged().collect` 跨页 setUp,仅在当前页 attach `GSYPlayerSurface`;setLooping(true) + setCacheWithPlay(true);emulator 实证 page 1 `Playing 1/5` + page 2 切页后 logcat `GSYVideoPlayer: onPrepared` 链路通 - [x] D10 FloatingWindowCompose ✅ [FloatingWindowComposeActivity.kt](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/app/src/main/java/com/example/gsyvideoplayer/compose/host/FloatingWindowComposeActivity.kt) — `rememberLauncherForActivityResult(StartActivityForResult())` 申请 `SYSTEM_ALERT_WINDOW` + `Util.hasPermission` 检测;复用 [FloatPlayerView](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/app/src/main/java/com/example/gsyvideoplayer/view/FloatPlayerView.java)(Wrapper 路线)证明 Compose Activity 同样能驱动全局画中画;onDestroy → `FloatWindow.destroy()`;emulator 实证 `权限状态:✅ 已授予 / 悬浮窗状态:▶ 已显示` - [x] D11 MoreTypeCompose ✅ [MoreTypeComposeActivity.kt](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/app/src/main/java/com/example/gsyvideoplayer/compose/host/MoreTypeComposeActivity.kt) — `enum CellType { Normal, Ad, Cover, Unknown }` 4 类 cell;6 条 entry LazyColumn;单 controller 仅 attach activeIndex;emulator 实证点 Normal cell 后 `当前激活:#1 样片 #1 · 普通视频 | Playing 1115/5547619 ms` + logcat `videoWidth: 480 videoHeight: 384` - [x] D14 WebDetailCompose ✅ [WebDetailComposeActivity.kt](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/app/src/main/java/com/example/gsyvideoplayer/compose/host/WebDetailComposeActivity.kt) — Column 上方 16:9 `GSYPlayerSurface + GSYDefaultControls`(Compose 区),下方 `AndroidView { WebView }` `Modifier.weight(1f)` 弹性占据剩余空间;emulator 实证 `当前:Playing | 1904/10027 ms` + logcat `libwebviewchromium_plat_support.so` 加载(WebView 渲染)+ `CURRENT_STATE_PLAYING` - [x] [ComposeDemoListActivity.kt](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/app/src/main/java/com/example/gsyvideoplayer/compose/ComposeDemoListActivity.kt) 16 → 20 项;4 个新 Activity 在 [AndroidManifest.xml](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/app/src/main/AndroidManifest.xml) 注册 `exported="false"` + configChanges 全集 - [x] R5 上半场真机回归 ✅ emulator-5554 装机;4 个新 demo 通过 UI Automator 真点击进入 + 起播实证;Monkey 100 events `--pct-touch 60 --pct-motion 30` 0 FATAL;logcat AndroidRuntime FATAL/crash buffer 全空 - [x] D12 AudioOnlyCompose ✅ [AudioOnlyComposeActivity.kt](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/app/src/main/java/com/example/gsyvideoplayer/compose/host/AudioOnlyComposeActivity.kt) — `LaunchedEffect { GSYVideoManager.instance().enableRawPlay(applicationContext) }` + `android.resource://$pkg/${R.raw.test3|test33}` 双 raw 音频源;圆形播放图标 + `${snapshot.state}` 状态显示 + `if (snapshot.isPlaying) onVideoPause() else onVideoResume()` 暂停/继续;**关键 bug 修复**:原设计无 GSYPlayerSurface 时 `controller.host = null` → `setUp` 是 no-op,故必须挂 `Modifier.size(1.dp)` 隐身 GSYPlayerSurface 让 controller 拿到 host;emulator 实证 `当前:本地 raw · test3.mp3 | Playing` + `进度:10677/173448 ms · buffer 99%` + logcat `Audio focus request granted` + `IjkMediaPlayer_native_setup` + `CURRENT_STATE_PLAYING` - [x] D13 LocalFileCompose ✅ [LocalFileComposeActivity.kt](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/app/src/main/java/com/example/gsyvideoplayer/compose/host/LocalFileComposeActivity.kt) — `OutlinedTextField` URL 输入 + `Checkbox` cache 开关 + 3 个预设源(HTTP `mov_bbb.mp4` / raw `test.mp4` / raw `test4.mp4`);起播 / Release / 填入预设 三按钮;emulator 实证 `00:07/00:10` + logcat `IjkMediaPlayer_start` + `CURRENT_STATE_PLAYING` + `FFP_MSG_AUDIO_SEEK_RENDERING_START` - [x] D15 MediaCodecCompose ✅ [MediaCodecComposeActivity.kt](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/app/src/main/java/com/example/gsyvideoplayer/compose/host/MediaCodecComposeActivity.kt) — `Switch` 实时切换 `GSYVideoType.enableMediaCodec()/enableMediaCodecTexture()` ↔ `disableMediaCodec()/disableMediaCodecTexture()`;`var hwOn by remember { mutableStateOf(GSYVideoType.isMediaCodec()) }`;`onDispose { runCatching { GSYVideoType.disableMediaCodec() } }` 离开页面还原;emulator 实证 `状态:✅ 已启用` + `Playing | 11554/5547619 ms · 480x384` + logcat `ffpipenode_create_video_decoder_from_android_mediacodec` + `MediaCodec: H264_HIGH: enabled` + `ffpipeline_select_mediacodec_l`(emulator 限制无真实 hw codec,IJK fallback 软解符合预期,硬解管线已激活) - [x] D16 CustomControlsThemeCompose ✅ [CustomControlsThemeComposeActivity.kt](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/app/src/main/java/com/example/gsyvideoplayer/compose/host/CustomControlsThemeComposeActivity.kt) — `enum class ControlTheme { Neon, Sunset, Mono }` 各带 fg/bg/`Brush.verticalGradient`;完全自绘 overlay 替代 GSYDefaultControls:渐变 Box + 圆形播放按钮 + Slider seek(dragging/dragValue 双通道)+ 主题切换条;`detectTapGestures(onTap = { overlayVisible = !overlayVisible })` 点画面切浮层;`formatMs()` 工具函数;emulator 实证 `Neon | Playing` + `00:12/92:27` + 主题点击切换响应 `Sunset | Playing` + logcat `onPrepared` + `CURRENT_STATE_PLAYING` + `videoWidth: 480 videoHeight: 384` - [x] [ComposeDemoListActivity.kt](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/app/src/main/java/com/example/gsyvideoplayer/compose/ComposeDemoListActivity.kt) 20 → 24 项;4 个新 Activity 在 [AndroidManifest.xml](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/app/src/main/AndroidManifest.xml) 注册 `exported="false"` + configChanges 全集 - [x] R5 下半场真机回归 ✅ emulator-5554 装机;4 个新 demo 通过 UI Automator 真点击进入 + 起播实证;Monkey 100 events `--pct-touch 60 --pct-motion 30` 0 FATAL ### 轮次 R6 — P5-Δ 老 demo 升级 + 文档收口 + 首发评估 - [x] ΔD1 / ΔD4 / ΔD5 / ΔD6 / ΔD7 升级 - ΔD1 [BasicWrapperActivity.kt](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/app/src/main/java/com/example/gsyvideoplayer/compose/host/BasicWrapperActivity.kt):builder 后追加 5 个高频选项 `setVideoAllCallBack` / `setSeekRatio`(0.5/1/2/3x 切换条)/ `setShowPauseCover`(Switch)/ `setReleaseWhenLossAudio`(Switch)/ `setStartAfterPrepared`;UI 实时显示 `回调状态:onPrepared #N`,emulator 实证起播后 `onPrepared #1(onPrepared 累计 1 次)` - ΔD4 [ListPlayNativeActivity.kt](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/app/src/main/java/com/example/gsyvideoplayer/compose/host/ListPlayNativeActivity.kt):加"ΔD4 演示开关"卡片(**离屏 setUp 重置 vs pause** + `setShowPauseCover`),未播放时 Compose 自绘占位封面(标题 + 渐变色块,等价 `setThumbImageView` 的 Native 路径替代方案);emulator 实证占位封面文案展示 + `点击播放` → `暂停 / 继续` + logcat `Net speed: 0 KB/s percent 8` 真起播 - ΔD5 [SwitchUrlActivity.kt](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/app/src/main/java/com/example/gsyvideoplayer/compose/host/SwitchUrlActivity.kt):顶部 KDoc see-also 注释指向 D8 SwitchSeamlessComposeActivity(说明 setUp 重置必丢 surface 接管,需要无缝接力请用 D8) - ΔD6 [MultiWindowActivity.kt](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/app/src/main/java/com/example/gsyvideoplayer/compose/host/MultiWindowActivity.kt):标题改"Native 多窗口 Demo(互斥版)"+ 顶部说明加"如需「真并行」请看 D7 多 CustomManager 实例并行版" - ΔD7 [AutoPlayListActivity.kt](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/app/src/main/java/com/example/gsyvideoplayer/compose/host/AutoPlayListActivity.kt):顶部 KDoc 解释 surface 接管取舍(events.AutoComplete 接力 setUp 必然丢 surface 接管 → 一次黑屏,业务零容忍请改 Wrapper + SwitchUtil 链路 / 等 P0-1 surface 共享层 GA) - [x] doc/COMPOSE_USE.md 加"九、能力矩阵:何时选 Wrapper / 何时选 Native"对照表(9 行常见诉求 × 2 模式 × ✅/⚠️/❌ 三档评级 + 简化决策三句话) - [x] doc/COMPOSE_USE.md 加"十、Cookbook:从 Java/XML 迁移 Compose"(5 个常见迁移模式:基本播放 / 全屏 / 列表 / 字幕回调封面 / 切源接力,每段附 Java→Compose 最小改造模板 + 仓库可点击 Activity 对照) - [x] **评估首发**:详见下方"首发评估结论 (R6)" #### 首发评估结论 (R6) **基线核对(截至本轮):** - 🟢 **P0 全过**(P0-1 surface 共享层降级落地 / P0-2 setUp 流式 builder / P0-3 全屏 API + 反射克隆 public 修复) - 🟢 **P1 全过**(events SharedFlow / stateFlow / 直 setter 8 项 / 用户回调 / Buffering+SeekComplete / ΔD3 手势) - 🟢 **P5-1 8/8** demo 已落(D1/D2/D3/D4/D5/D6/D7/D8 全部 emulator 真 UI 点击实证起播) - 🟢 **P5-2 8/8** demo 已落(D9/D10/D11/D12/D13/D14/D15/D16 全部 emulator 真起播实证) - 🟢 **P5-Δ 5/5**(ΔD1/ΔD4/ΔD5/ΔD6/ΔD7 R6 升级,emulator 装机 + 5 demo 启动成功 + ΔD1/ΔD4 真起播实证 + Monkey 100 events 0 FATAL + crash buffer 空 + 1 个非 Compose 防回归对照 DetailNormalActivityPlayer) - 🟢 **文档**:[COMPOSE_USE.md](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/doc/COMPOSE_USE.md) 10 章齐全(含能力矩阵 + cookbook);README 中英文 Compose 章节"未发布"提示块仍在 - 🟢 **构建**:双通道 `:app:assembleDebug` + `:gsyVideoPlayer-compose:assembleDebug` + `:gsyVideoPlayer-compose:assembleRelease` + `publishToMavenLocal` 全 BUILD SUCCESSFUL,diagnostics 0 error **结论:✅ 满足"去掉未发布标识"的全部前置条件**,但**本轮按既定纪律不打 tag** —— 上限是把 [README.md](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/README.md) / [README_CN.md](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/README_CN.md) 中"未发布(Unreleased)"提示块改为"已 GA"+ [doc/COMPOSE_BACKLOG.md](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/doc/COMPOSE_BACKLOG.md) 顶部状态线同步(建议后续单独一轮 G "Go-Live" 处理:单独 commit 改 README、再走一次双通道回归后正式打 tag)。本轮 R6 仅产出"评估通过"结论 + 文档完备,**留 G 轮去掉未发布标识 + 打 v8.x.x tag**。 **仍未做(不阻塞首发):** - R7 长尾:D17~D22 + PiP / AliPlayer / HTTP DNS Compose 端示范(差异化能力,非首发必备) - 真 P0-1 实现层:当前是"降级落地"([GSYComposePlayer + GSYPlayerController + GSYPlayerSurface](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/gsyVideoPlayer-compose/src/main/java/com/shuyu/gsyvideoplayer/compose/native_))已能撑住 24 demo,但跨 Activity surface 真共享接管层仍是后续优化空间(ΔD7 KDoc 已落地"已知短板"说明) ### 轮次 R7(可选)— P5-3 长尾 demo + AliPlayer / PiP / HTTP DNS Compose 端"新增能力示范" - [ ] D17 ~ D22 - [ ] PiP(Compose 端首发示范,Java 端也没有,作为差异化卖点) --- ## 3. 进度跟踪 | 轮次 | 状态 | 起止 commit | 备注 | |---|---|---|---| | R1 | ✅ 已完成 | `24360bff` (归档 plan 落盘) → `276420b7` (R1 修复) | P2 六项全过;构建 + 模拟器双回归通过;不发 tag | | R2 | ✅ 已完成 | `276420b7` (R1 修复) → `6f5f846c` (R2 修复) | P0-1/2/3 三项全过;额外修复 GSYComposeHostPlayer public class+构造器(反射克隆门票);构建 + 模拟器双回归通过;不发 tag | | R3 | ✅ 已完成 | `6f5f846c` (R2) → `fe66e7fd` (R3 P1 reactive parity) | P1 五项全过;events SharedFlow + stateFlow + setUserVideoAllCallBack + 8 直 setter + BufferingProgress/SeekComplete + ΔD3 手势;emulator Monkey 50 events 0 FATAL | | R4 | ✅ 已完成(8/8 demo) | `fe66e7fd` (R3) → `47ce1877` (R4 上半场 D1/D2/D4/D8) → 本轮 (R4 续 D3/D5/D6/D7) | P5-1 全部 8 项已落(滤镜 / 缓存下载 / 前贴片广告 / 字幕 / 自绘弹幕 / EXO 多源 / Wrapper 真并行 / Seamless 切换);ComposeDemoListActivity 8 → 16 项;emulator 真 UI 点击实证:D6 EXO 内核切换 logcat 实证 + D5 长视频 Playing 48s/5547s + D3 ad→feature 阶段切换 + D7 真起播 0 crash;Monkey 100 events 0 FATAL | | R5 | ✅ 已完成(8/8) | `47ce1877` (R4 上半场) → `d05d0a8c` (R4 续 D3/D5/D6/D7) → `eceebd31` (R5 上半场 D9/D10/D11/D14) → 本轮 (R5 下半场 D12/D13/D15/D16) | P5-2 全部 8 项已落(VerticalPager 短视频 / 悬浮窗 + 系统权限申请 / 多类型 cell 列表 / WebView 图文混排 / 纯音频 raw + 1dp 隐身 Surface bug 修 / URL 输入起播 / MediaCodec 硬解 Switch 切换 / 自绘主题化 controls overlay);ComposeDemoListActivity 16 → 20 → 24 项;emulator 真 UI 点击实证 8 个 demo 全起播:D9 page1 `Playing 1/5` / D10 `悬浮窗状态:▶ 已显示` / D11 `Playing 1115/5547619 ms` / D14 `Playing 1904/10027 ms` + libwebviewchromium / D12 `Playing 10677/173448 ms` + IjkMediaPlayer_native_setup / D13 `00:07/00:10` HTTP `mov_bbb.mp4` / D15 `Playing 11554/5547619 ms · 480x384` + `MediaCodec: H264_HIGH: enabled` / D16 `Sunset/Neon Playing 00:12/92:27` + 主题切换响应;上下半场各 Monkey 100 events 0 FATAL;首发基线达成 | | V | ✅ 已完成 | (R5 之后) → 本轮 | Demo URL 中央化:新建 [DemoVideoUrls.java](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/app/src/main/java/com/example/gsyvideoplayer/utils/DemoVideoUrls.java) 中央常量表(8 条可达 URL + 8 条语义别名);39 Java + 6 Kotlin 文件全量替换硬编码 URL;4 条不可达 URL(vorwaerts BBB / IMG_0382 / flipfit / 7xjmzj)按协议同语义替换为 MP4_BBB/HLS_MUX;清理 5 个文件中 9 行 dead URL 注释 + DetailPlayer.java L617-L681 注释墓地;双通道构建 + Monkey 100 events 0 FATAL;详见 [doc/VIDEO_URLS.md](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/doc/VIDEO_URLS.md) | | R6 | ✅ 已完成 | (V 之后 `ae0c37ba`) → 本轮 | P5-Δ 5/5 老 demo 升级(ΔD1 5 高频 builder 选项 + UI 回调显示 / ΔD4 离屏 setUp 重置 vs pause + setShowPauseCover Switch + Compose 自绘占位封面 / ΔD5 see-also D8 / ΔD6 标题"互斥版" + D7 跳转提示 / ΔD7 KDoc surface 接管取舍);[COMPOSE_USE.md](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/doc/COMPOSE_USE.md) 加 § 9 能力矩阵 + § 10 Cookbook;双通道构建 BUILD SUCCESSFUL;emulator-5554 装机 + 5 ΔActivity 全启动 + ΔD1 实证 `onPrepared #1` + ΔD4 实证 `Net speed: 0 KB/s percent 8` + 防回归对照 DetailNormalActivityPlayer + Monkey 100 events 0 FATAL + crash buffer 空;**首发评估通过**(结论以 doc 形式落地,按纪律本轮不打 tag,留 G 轮 Go-Live) | | H | ✅ 已完成 | `ae0c37ba` (R6) → `1e935452` (H 轮 P0 修复) | H 轮 P0 fixes for library, demo and docs:补齐 Compose 库与 Demo 的小修补;Java/Compose 双通道构建 + emulator 模拟器回归通过;不发 tag | | I-1 | ✅ 已完成 | `1e935452` (H) → `74c8a0eb` (Controller 拆分) | `GSYPlayerController.release()` / `dispose()` 拆分(避免 Compose 重组时误销毁 host);构建 + emulator 双回归通过;不发 tag | | I-2 | ✅ 已完成 | `74c8a0eb` (I-1) → `c8ce3203` (PLAYBOOK + 脚本) → `90ab81a8` (PLAYBOOK 录入 I-2 结果 + Wrapper 自动播放注解修订) | 新增 [doc/COMPOSE_TEST_PLAYBOOK.md](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/doc/COMPOSE_TEST_PLAYBOOK.md) + 配套可复用 shell/python 脚本;I-2 全量 Compose 回归(24 demo 真 UI 实证 + Monkey 0 FATAL)+ 修订 Wrapper 自动播放说明;不发 tag | | I-3 | ✅ 已完成 | `90ab81a8` (I-2) → 本轮 | 文档全量回归(37 份 markdown):修复 [README_CN.md](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/README_CN.md) 坏链 `README_EN.md` → `README.md`;4 份发布文档示例版本 `11.3.0` → `13.0.0`([ARCHITECTURE.md](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/doc/ARCHITECTURE.md) 5 处 / [DUAL_CHANNEL_PUBLISH.md](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/doc/DUAL_CHANNEL_PUBLISH.md) 8 处 / [README_DEPENDENCY_GUIDE.md](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/doc/README_DEPENDENCY_GUIDE.md) 6 处 / [MAVEN_CENTRAL_AUTOMATION.md](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/MAVEN_CENTRAL_AUTOMATION.md) 2 处);本表 §0 校对时间 + §3 进度表追加 H/I/I-2/I-3 四轮;纯文档变更,按纪律豁免模拟器回归;不发 tag | | J | ✅ 已完成 | `9cafdb08` (I-3) → 本轮 | **Java 端 4 项基础能力真机回归**(用户犀利质疑触发立项:18 轮自动化只测过 Compose,Java 端从未真测过 A 播放暂停 / B 拖进度 / C 全屏 / D 切换内核)。新增 [doc/JAVA_TEST_PLAYBOOK.md](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/doc/JAVA_TEST_PLAYBOOK.md) + [doc/test_scripts/java_basic_regression.sh](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/doc/test_scripts/java_basic_regression.sh)(一键 6/6 PASS) + [doc/test_scripts/java_cold_smoke.sh](file:///Users/guoshuyu/workspace/android/GSYVideoPlayer/doc/test_scripts/java_cold_smoke.sh)(39 entry 38/39 PASS,唯一 FAIL 为 toggle 按钮非跳页)。**DetailPlayer 端到端 logcat 实证**:A `onClickStop`/`onClickResume`/`CURRENT_STATE_PAUSE`/`CURRENT_STATE_PLAYING`;B `onClickSeekbar`/`onSeekComplete`,progress 12%→80% / currentPosition 226489ms→1458000ms;C `orientation=landscape` + `WindowManager: finishDrawing of orientation change DetailPlayer 141ms`;D 三态文字循环 IJK→EXO→系统 + 切到 EXO 后 logcat 真实出现 `ExoPlayerImpl: Init [AndroidXMedia3/1.10.1]`,与默认 IJK 状态的 `nativeloader: libijkffmpeg.so` 形成内核切换底层实证。5 个代表 Activity(DetailPlayer / DetailListPlayer / DetailMoreType / DetailFilter / DetailADPlayer2)跑出真实差异:DetailListPlayer/MoreType/AD2 的暂停按钮坐标与 DetailPlayer 不同、DetailFilter `progress` 控件 ID 不同 → 后续 K 轮承接为各 Activity 提供 layout 适配映射。识别并文档化 5 个真实自动化坑(Activity 不 exported / resource-id 包名前缀 / textAllCaps / controls 自动隐藏 / sed 贪婪匹配);不发 tag | | R7 | ☐ pending | — | 选做 | > 每轮完成后,将本表格状态 ☐ 改为 ✅ 并附 commit hash;同时把 § 1 / § 2 中已完成项的 `[ ]` 改为 `[x]`。 --- ## 4. 备忘 / 不收口的决策 - **Compose 模块在 P5-1 完成前,仍维持"未发布"状态**:不打 tag,不在 Maven Central 上放 13.0.0。 - **PR 粒度建议**:每轮一个独立 PR(或一组合并 commit),便于回滚;commit message 用 `compose: R ...` 前缀串起跟踪。 - **真机回归不可省**:每轮至少跑受影响 demo + 1 个非 Compose Activity(保证不破坏老功能),并核对 crash buffer 与 ANR。 - **Code Reference 维护**:当 Native 模式新增公开 API(`withHost` / `enterFullscreen` / `events` 扩项 / 直 setter)时,每条都应在 doc/COMPOSE_USE.md 给出可点击的 file 链接,便于贡献者跟读。