# 需求进度

## 当前总目标
- 控制当前 Flutter Android 发包体积，持续定位冗余组件、超大资源和不合理构建配置，并把每一步处理结果落盘记录。

## 已完成模块
- 创建并持续维护进度跟踪文件。
- 已修复首页房间封面显示链路：兼容接口 `roomCover/cover` 双字段，并补齐编辑房间成功后的封面内存回写。
- 完成仓库结构、依赖引用、资源体积和 APK 组成的第一轮排查。
- 移除 4 个当前未在业务层直接使用的插件依赖：`loading_indicator_view_plus`、`social_sharing_plus`、`flutter_foreground_task`、`on_audio_query`，并清理相关平台声明。
- 修补 `image_cropper 5.0.1` Android 兼容问题，切换到本地 path 依赖以恢复构建。
- 完成 Android release 签名回退配置，解决仓库缺少 `yumi.jks` / `yumi_debug.jks` 时无法继续本地构建分析的问题。
- 完成 release 包体分析，确认 universal APK 过大主要由多 ABI 与 Agora/Tencent 原生 so 导致。
- 去除 Gradle 中与 Flutter 冲突的硬编码 `abiFilters`，确认 `--split-per-abi` 可以正常产出分架构 APK。
- 完成 TinyPNG 环境核查，并接入可执行的 Ruby 批量压缩脚本。
- 已完成 TinyPNG 第一轮批量压缩，`sc_images + assets` 总量已从 `47.25 MB` 降到 `22.54 MB`。
- 已增强 TinyPNG 脚本，支持断点续跑、单文件目标和失败项重试。
- 已完成 TinyPNG 网络失败项补跑，6 张 `SSL_connect` 异常图片已全部压缩成功。
- 已完成图片压缩后的 release 复测，确认分 ABI APK 体积继续显著下降。
- 已切换 Android `release` 为真正的瘦身模式：开启 `minify`、`shrinkResources`、`zipAlign`。
- 已完成开启瘦身配置后的 Android release 复测，并接入 `split-debug-info` 保存 Dart symbols。
- 已生成统一的 Python 打包脚本 `scripts/build_release.py`，开始接管 `AAB`、分 ABI APK 与 iOS 包流程。
- 已完成 `scripts/build_release.py` 的 Android / iOS 真机冒烟验证，产物归档目录与 manifest 均已生成。
- 已按当前需求再次完成正式 `AAB` 出包。
- 已定位 Google Play 上传阻塞点：当前 `AAB` 使用的是 `Android Debug` 证书签名，不是正式 upload key。
- 已确认当前项目属于“首次上架的新应用”，后续应走 Play App Signing 首发流程，而不是兼容老签名迁移流程。
- 已梳理 Google Play 首次上架所需的 `Play App Signing`、`upload key`、重新打包与提审步骤。
- 已澄清首次生成 `upload keystore` 时的“密钥库口令”是本地自行设置的密码，与 Google 提供的 `deployment_cert.der` 证书文件无关。
- 已为新应用生成正式 `upload keystore`，并接入工程的 release 签名配置。
- 已重新打出正式 `AAB`，并确认其签名已经从 `Android Debug` 切换到新的 upload key。
- 已定位 `FOREGROUND_SERVICE_MEDIA_PROJECTION` 的来源为 Agora `full-screen-sharing` 依赖，并开始从主 Manifest 覆盖移除。
- 已移除 `FOREGROUND_SERVICE_MEDIA_PROJECTION` 相关声明并重新打出正式 `AAB`。
- 已按当前需求打出正式 `arm64-v8a` 单架构 `APK`。
- 已将新的 `google-services.json` 替换进工程，准备重新打包验证。

## 进行中模块
- 评估 iOS 正式签名参数接入后导出 `.ipa` 的流程。
- 继续评估 Agora 扩展 so 与 release shrink 策略是否还能进一步裁剪。
- 评估 12 张 TinyPNG 不支持解码的特殊 `webp` 是否需要改格式或换工具处理。

## 关键技术决策
- 先做高置信度瘦身：优先清理未使用插件、ABI 配置和超大图片，暂不贸然删除核心业务能力。
- 避开仓库中用户已有未提交改动，只改必要文件并持续把结果写入本文件。
- 包体分析聚焦 release 产物，不被 `build/`、iOS 中间产物等本地缓存误导。
- Android 架构裁剪交给 Flutter 构建命令驱动，不再在 Gradle 中硬编码 `abiFilters`。
- 对 `image_cropper` 采用本地 path 依赖加最小补丁策略，避免升级带来的业务回归面。
- Android 签名配置采用条件回退：正式 keystore 不存在时自动回退到 debug 签名，仅用于本地分析验证。
- 图片压缩优先走 TinyPNG，以尽量保持画质稳定；失败项按原因分流处理。
- TinyPNG 的 `SSL_connect` 视为可重试网络抖动；`Image could not be decoded` 视为 TinyPNG 对特殊/动图 WebP 的能力边界，先记录不阻塞其余压缩。

## 已改动文件
- `需求进度.md`
- `lib/services/room/rc_room_manager.dart`
- `lib/shared/business_logic/models/res/room_res.dart`
- `lib/shared/business_logic/models/res/follow_room_res.dart`
- `lib/shared/business_logic/models/res/my_room_res.dart`
- `lib/shared/business_logic/models/res/sc_edit_room_info_res.dart`
- `lib/shared/business_logic/models/res/join_room_res.dart`
- `.gitignore`
- `android/key.properties`
- `pubspec.yaml`
- `pubspec.lock`
- `lib/ui_kit/components/dialog/dialog.dart`
- `android/app/src/main/AndroidManifest.xml`
- `android/app/google-services.json`
- `ios/Runner/Info.plist`
- `scripts/tinypng_batch.rb`
- `scripts/build_release.py`
- `tinypng-progress.json`
- `tinypng-report.json`
- `local_packages/image_cropper-5.0.1-patched/android/src/main/java/vn/hunghd/flutter/plugins/imagecropper/ImageCropperPlugin.java`
- `local_packages/image_cropper-5.0.1-patched/`
- `android/app/build.gradle.kts`
- `android/app/upload-keystore.jks`
- `android/app/upload_certificate.pem`
- `build/symbols/android/`
- `build/symbols/ios/`
- `dist/release/smoke-android/`
- `dist/release/smoke-ios/`
- `sc_images/`
- `assets/`

## 已验证结果
- 当前工作区存在用户已有未提交改动，后续处理均避开覆盖。
- 首页 `party/follow/history/mine` 以及房间详情使用的房间封面模型已兼容 `roomCover` 与 `cover` 两种返回字段，上传封面后不会再因为字段名不一致掉回空占位。
- 目录体积排查显示：`build/` 约 `4.7G`、`.dart_tool/` 约 `347M`、`ios/` 约 `250M`、`sc_images/` 约 `48M`、`local_packages/` 约 `6.1M`。
- 当前包体过大的主要原因已经确认：
- universal APK 带入了 `arm64-v8a`、`armeabi-v7a`、`x86_64` 三套 ABI。
- Agora 与腾讯 IM 原生 so 很大，其中 `libagora-rtc-sdk.so` 合计约 `79.4MB`，`libImSDK.so` 合计约 `10.2MB`。
- 资源目录中 `sc_images/index`、`sc_images/room`、`sc_images/person`、`sc_images/splash` 是最主要的大图来源。
- `flutter build apk --release --target-platform android-arm64 --analyze-size` 已成功执行，当前 universal release APK 约 `271MB`。
- `flutter build apk --release --split-per-abi` 已成功执行，压缩前分架构 APK 约为：
- `app-arm64-v8a-release.apk` 约 `161.4MB`
- `app-armeabi-v7a-release.apk` 约 `138.8MB`
- `app-x86_64-release.apk` 约 `148.6MB`
- `loading_indicator_view_plus`、`social_sharing_plus`、`flutter_foreground_task`、`on_audio_query` 已从当前依赖链路中移除。
- 当前图片资源统计为 `412` 张，TinyPNG 两轮处理后总量已从 `47.25 MB` 降到 `22.27 MB`，累计节省约 `24.99 MB`。
- 当前 TinyPNG 已成功压缩 `400` 张图片，剩余失败 `12` 张。
- 当前压缩收益最大的图片包括：
- `sc_images/person/sc_icon_edit_userinfo_bg.png`：`2640776 -> 436823`，节省约 `2.10 MB`
- `sc_images/splash/sc_splash.png`：`2373335 -> 379804`，节省约 `1.90 MB`
- `sc_images/room/sc_icon_room_defaut_bg.png`：`2243222 -> 704696`，节省约 `1.47 MB`
- `sc_images/index/sc_icon_index_bg.png`：`1422169 -> 420635`，节省约 `0.96 MB`
- 失败项已分型：
- `12` 张为 TinyPNG 无法解码的特殊/动图 `webp`
- 当前 TinyPNG 进度与统计文件已落盘：`tinypng-progress.json`、`tinypng-report.json`
- 图片压缩后再次执行 `flutter build apk --release --split-per-abi`，当前分架构 APK 已下降到：
- `app-arm64-v8a-release.apk` 约 `135.2MB`，比压缩前少 `26.2MB`
- `app-armeabi-v7a-release.apk` 约 `112.6MB`，比压缩前少 `26.2MB`
- `app-x86_64-release.apk` 约 `122.4MB`，比压缩前少 `26.2MB`
- 可以确认：这轮图片压缩带来的资源收益已经真实反映到最终 APK 体积中。
- 在开启 `minify`、`shrinkResources`、`zipAlign` 并追加 `--split-debug-info=build/symbols/android` 后，Android release 继续下降为：
- `app-release.aab` 约 `181.8MB`
- `app-arm64-v8a-release.apk` 约 `128.7MB`，比上一轮 `135.2MB` 再少 `6.5MB`
- `app-armeabi-v7a-release.apk` 约 `105.8MB`，比上一轮 `112.6MB` 再少 `6.8MB`
- `app-x86_64-release.apk` 约 `115.8MB`，比上一轮 `122.4MB` 再少 `6.6MB`
- `build/symbols/android` 已生成 3 份符号文件，总计约 `14MB`，后续可用于 Dart 堆栈还原。
- `scripts/build_release.py --platform android --output-dir dist/release/smoke-android` 已验证通过，归档结果为：
- `android/google-play/` 下输出 `yumi-v1.0.0-b1-google-play.aab` 与 Dart symbols
- `android/local/` 下输出 `arm64-v8a.apk` 与 `armeabi-v7a.apk`
- `android/testing/` 下单独输出 `x86_64-test.apk`
- 同时生成 `build_manifest.json`，已记录产物路径、大小和 `sha256`
- `scripts/build_release.py --platform ios --output-dir dist/release/smoke-ios` 已验证通过，当前无签名模式下输出：
- `ios/archive/yumi-v1.0.0-b1.xcarchive`
- `ios/symbols/app.ios-arm64.symbols`
- 当前 iOS 无签名构建会跳过 `.ipa` 导出；如需正式 `.ipa`，脚本需追加 `--ios-codesign`
- 已将 `dist/` 加入 `.gitignore`，避免打包产物持续污染工作区状态
- 本轮按需执行 `flutter build appbundle --release --split-debug-info=build/symbols/android` 已成功，产物为：
- `build/app/outputs/bundle/release/app-release.aab`
- 当前 AAB 体积约 `181.8MB`
- 本地校验 `app-release.aab` 证书后确认：
- 证书所有者为 `C=US, O=Android, CN=Android Debug`
- 当前 AAB 的 `SHA1` 为 `6B:72:BF:6F:D1:7A:F2:99:CD:F3:14:EE:18:A6:29:67:F8:05:E6:B4`
- 这说明当前 release 构建仍在回退使用 debug signing，无法直接作为 Google Play 正式发布包
- 已生成新的 upload 证书文件：
- `android/app/upload-keystore.jks`
- `android/app/upload_certificate.pem`
- 新 upload key 当前指纹为：
- `SHA1: C1:80:AF:BF:6E:1F:F2:F4:49:58:72:87:A2:BC:54:07:E7:D2:D9:A3`
- `SHA256: 81:D6:3A:43:31:FA:83:87:0E:C3:5B:52:B8:C1:05:7C:3A:32:EC:24:37:0B:11:E4:2F:8C:62:4E:D4:AC:0C:3B`
- release 构建已改为从 `android/key.properties` 读取正式 upload keystore，不再依赖当前的 debug 回退配置
- `flutter build appbundle --release --split-debug-info=build/symbols/android` 已重新执行，当前正式产物仍为：
- `build/app/outputs/bundle/release/app-release.aab`
- 当前 AAB 体积约 `181.8MB`
- 重新校验该 AAB 后，签名者已变为：
- `CN=Yumi Upload, OU=Mobile, O=Yumi, L=Shanghai, ST=Shanghai, C=CN`
- `deployment_cert.der` 已核实为 Google 侧 app signing 证书，指纹为：
- `SHA1: 3F:E1:78:DC:C3:29:4F:74:20:DF:27:54:CF:AF:46:46:D6:A1:94:78`
- `SHA256: F9:BF:D0:89:F9:D7:FD:B2:53:24:A7:DB:59:E1:52:6F:54:A3:93:95:4E:88:7F:1C:4B:D6:03:66:E7:1A:1F:85`
- 已确认当前存在两套不同证书：
- 本地 `upload key`：用于你上传 AAB 到 Google Play
- Google `app signing key`：用于 Google Play 最终给用户分发签名包
- `FOREGROUND_SERVICE_MEDIA_PROJECTION` 在当前 release 合并报告中的来源已确认是 `io.agora.rtc:full-screen-sharing:4.5.2`
- 该依赖同时还带入了：
- `io.agora.rtc2.extensions.MediaProjectionMgr$LocalScreenCaptureAssistantActivity`
- `io.agora.rtc2.extensions.MediaProjectionMgr$LocalScreenSharingService`
- 主 Manifest 已添加 `tools:node="remove"` 覆盖规则，准备重新打包验证最终合并结果
- 重新打包后，release manifest merger 报告已显示：
- `uses-permission#android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION`
- `REJECTED from [io.agora.rtc:full-screen-sharing:4.5.2]`
- 当前 release 中已不再检测到以下声明：
- `android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION`
- `io.agora.rtc2.extensions.MediaProjectionMgr$LocalScreenCaptureAssistantActivity`
- `io.agora.rtc2.extensions.MediaProjectionMgr$LocalScreenSharingService`
- 新的正式产物已重新生成：
- `build/app/outputs/bundle/release/app-release.aab`
- 当前 AAB 体积仍约 `181.8MB`
- 新 AAB 证书仍为本地 upload key：
- `SHA1: C1:80:AF:BF:6E:1F:F2:F4:49:58:72:87:A2:BC:54:07:E7:D2:D9:A3`
- 本轮按需执行 `flutter build apk --release --split-per-abi --target-platform android-arm64 --split-debug-info=build/symbols/android` 已成功，产物为：
- `build/app/outputs/flutter-apk/app-arm64-v8a-release.apk`
- 当前 `arm64-v8a` APK 体积约 `128.7MB`
- 已核对新旧 `google-services.json`：
- 原文件与新文件都匹配包名 `com.org.yumi`
- 下载文件已成功覆盖到 `android/app/google-services.json`
- 当前工程内文件 `SHA256` 为：
- `a1706496a01f74d27e6c60598144cecc978934b02a87567e1ced887b5b0185d5`

## 已知问题
- `ios/Podfile.lock` 还保留旧插件记录，因为本轮未执行 `pod install`；但 Flutter 当前依赖链路已经不再包含已移除插件。
- universal APK 仍然会非常大；当前正确发包方式应优先使用 `--split-per-abi` 或直接产出 `aab`。
- 仍有 `12` 张 `webp` 被 TinyPNG 拒绝解码，推测是动画或特殊编码格式，不能继续直接走 TinyPNG 常规压缩。
- `split-debug-info` 会把 Dart symbols 额外落到构建目录，发包时需要和正式包一起留档，不能丢。
- iOS 当前验证走的是 `--no-codesign`，因此只产出 `.xcarchive`，不会自动导出可直接分发的 `.ipa`。
- Flutter/Xcode 当前给出两条 iOS 提醒：
- 未来 iOS 版本将要求 `UIScene` 生命周期支持
- Launch image 仍是默认占位资源，提交前建议替换
- `android/key.properties` 保存了本地 upload keystore 口令，必须自行妥善备份，且不要提交到仓库
- 由于已显式移除 Agora 本地屏幕共享相关组件，如果业务后续要启用“屏幕共享/录屏推流”，需要再单独恢复相关 manifest 声明

## 下一步要做什么
- 将新的 `AAB` 上传到 Play Console，并在首发流程中继续使用 Google 管理的 app signing key。
- 在需要配置 Google 登录、Firebase、支付等生产环境指纹时，优先登记 Google 的 `deployment_cert.der` 指纹；如平台要求上传者证书，再额外登记本地 upload key 指纹。
- 如果需要正式 iOS 分发包，为 `scripts/build_release.py` 补入 `--ios-codesign` 所需签名参数并导出 `.ipa`。
- 继续评估 Agora 扩展库是否都需要，优先核查 `lip sync`、`spatial audio`、`clear vision`、`segmentation`、`face capture` 等扩展 so 能否裁剪。
- 决定是否处理剩余 12 张特殊 `webp`：改格式、换压缩工具，或维持现状。
