feat: 新增仪表盘页面与macOS多项优化

1. 新增TDashboard翻译类型与多语言文案
2. 完善macOS权限管理与Impeller渲染适配
3. 更新服务器部署配置与协议文件上传脚本
4. 修复翻译导入服务与根类型编译问题
This commit is contained in:
Developer
2026-06-26 06:34:05 +08:00
parent bd937b02f3
commit 88a3f6d65f
39 changed files with 4257 additions and 302 deletions

View File

@@ -6,6 +6,442 @@
***
## [v6.134.0] - 2026-06-26
### 🛠 修复App Store 审核合规 - Guideline 2.4.5(i) / 2.1(a)
#### 背景
App Store 审核驳回 v6.6.25 (2606260),提出 3 项问题:
1. **2.4.5(i)** 三个 entitlement 缺少匹配功能:`files.downloads.read-write` / `files.downloads.read-only` / `network.server`
2. **2.4.5(i)** 沙盒缺少必需 entitlement`device.camera`(相机功能)/ `personal-information.location`(定位功能)
3. **2.1(a)** 权限管理页点击「相册/存储/麦克风请求」时显示错误(`MissingPluginException`
#### 1. Entitlements 文件修订macOS 沙盒合规)
**`macos/Runner/Release.entitlements` + `DebugProfile.entitlements`**
| 操作 | Entitlement | 说明 |
|---|---|---|
| 新增 | `com.apple.security.device.camera` | 相机:扫码/拍照生成卡片/OCRAVCaptureDevice via PermissionManager.swift |
| 新增 | `com.apple.security.device.microphone` | 麦克风:语音录制/转文字AVCaptureDevice via PermissionManager.swift |
| 新增 | `com.apple.security.personal-information.location` | 定位permission_handler 依赖链引用 CoreLocation 符号,沙盒需此 entitlement |
| 删除 | `com.apple.security.files.downloads.read-only` | 代码无 `getDownloadsDirectory` 调用,确属冗余 |
| 删除 | `com.apple.security.files.downloads.read-write` | 同上 |
| ✅ 保留 | `com.apple.security.network.server` | LocalSend 局域网文件传输实际使用:`HttpServer.bindSecure` / `ServerSocket.bind` |
#### 2. Info.plist 补全 Usage Description
- 新增 `NSLocationUsageDescription`声明定位用途IP 大致区域,不获取精确 GPS
- 已存在:`NSCameraUsageDescription` / `NSMicrophoneUsageDescription` / `NSPhotoLibraryUsageDescription`
#### 3. 权限管理 Bug 修复(替代 permission_handler_apple 的 macOS 空实现)
**根因**`permission_handler_apple 9.4.9` 仅支持 iOS无 macOS 实现,调用 `Permission.camera.status` 等方法时抛出 `MissingPluginException`UI 显示错误提示。
**修复方案**:新增原生权限管理器,通过 MethodChannel 暴露给 Dart 端。
- ** `macos/Runner/PermissionManager.swift`(新文件)**
- 支持 4 类权限:`camera` / `microphone` / `photos` / `notification`
- 基于 AVFoundation / Photos / UserNotifications framework
- 状态字符串与 Dart 侧 `AppPermissionStatus` 枚举对齐:`notDetermined` / `granted` / `permanentlyDenied` / `restricted`
- `openSystemSettings` 跳转「隐私与安全性」面板macOS 无法深链到子页面)
- **`macos/Runner/AppDelegate.swift`**
- 注册 MethodChannel `apps.xy.xianyan/macos.app` 三个方法:
- `checkPermission``PermissionManager.checkStatus`
- `requestPermission``PermissionManager.requestPermission`
- `openPermissionSettings``PermissionManager.openSystemSettings`
- **`macos/Runner.xcodeproj/project.pbxproj`**
- 注册 `PermissionManager.swift` 到 Runner targetPBXBuildFile + PBXFileReference + Sources
- **`lib/core/services/auth/permission_service.dart`**
- `AppPermission.macosPermissionName` 新增属性:返回原生权限名称字符串
- `AppPermission.localServer` 新增虚拟权限:用于 UI 解释 `network.server` entitlement 对应的 LocalSend 功能
- `AppPermission.isPlatformRelevant` 全平台适配macOS/Windows/Linux/Web 各自过滤
- `PermissionService.checkStatus` macOS 分支:调用 `MacosPlatformService.checkPermission`
- `PermissionService.requestPermission` macOS 分支:调用 `MacosPlatformService.requestPermission`
- **`macos/Runner/MainFlutterWindow.swift`**
- Impeller 开关修复setenv 方式已废弃,改为 `FlutterDartProject.commandLineArguments` 传递 `--enable-impeller`/`--no-enable-impeller`
#### 4. App Store Connect 回复要点
- `network.server`LocalSend 局域网文件传输HttpServer.bindSecure / ServerSocket.bind
- **用户入口路径**:① "我的" → "我的设备" → "文件传输助手"卡片
② "发现" tab → "文件传输助手"会话(📁 emoji
③ Spotlight 搜索 "文件传输" → 跳转
④ 二维码扫描 `xianyan://transfer` scheme
- `downloads.read-*`:已删除(无匹配功能)
- `device.camera` / `personal-information.location`:已添加
- `device.microphone`补加PermissionManager.swift 用到 AVCaptureDevice audio
- Bug 修复:`permission_handler_apple` 无 macOS 实现,已替换为原生 `PermissionManager`
#### 5. 防御性决策记录
- **麦克风 entitlement补加**Apple 仅点名 camera`PermissionManager.swift` 同时调用 `AVCaptureDevice.requestAccess(for: .audio)`,没麦克风 entitlement 同样会被 TCC 拒绝。一并加上避免下次因麦克风被拒再次返工。
- **定位 entitlement防御性添加**:闲言 APP 实际不用 GPS仅因 `permission_handler` 二进制含 CoreLocation 符号被 Apple 静态扫描到。更彻底的做法是 fork 该插件移除 location 部分,但成本高,添加 entitlement 是性价比最高的方案。
- **LocalSend 入口路径**:用户可通过 4 个入口触发文件传输功能(详见上方"用户入口路径"),向 Apple 解释 `network.server` 的实际使用场景。
#### 影响范围
- ✅ macOS 端权限管理页面可正常请求相机/麦克风/相册/通知权限
- ✅ 沙盒合规,所有 entitlement 都有匹配功能或代码引用
- ✅ 不影响 iOS / Android / 鸿蒙 / Windows / Linux 端
- ⚠️ 注意:删除 downloads entitlement 后,若未来需要"保存到下载目录"功能需重新添加
***
## [v6.133.0] - 2026-06-26
### ✨ 新增(隐私政策多端对齐 + 关于页 DUNS 入口 + GDPR 欧盟代表澄清)
#### 背景
v6.132.0 在隐私政策中新增了「主体信息」章节披露 D-U-N-S 编号,但遗留 4 个待办:① 关于页未同步展示 DUNS,多端不一致;② 「十二、国际数据保护」声明已指定欧盟代表但未披露具体身份,违反 GDPR Art.27;③ 网页英文区比中文区少 8 个章节(历史遗留不对齐);④ App 内隐私政策简版(9 章)与网页详版(16+ 章)内容详细程度不一致。本次一次性解决全部 4 项。
#### 1. 任务A:关于页同步展示 D-U-N-S
**新增 2 个翻译字段到 `t_about.dart`**:
- `entityInfoMenu` / `entityInfoMenuDesc` — 用于关于页「法律信息」区块的新菜单项
- 14 个语言文件全部补全(zh_CN/zh_TW/ja/ko/de/it/es/fr/pt/ru/ar/bn/hi/en)
- 法语/意大利语译文含撇号,使用双引号包裹
**`about_page.dart` _LegalSection 新增菜单项**:
- 图标 `CupertinoIcons.building_2_fill` — 主体信息入口
- 点击触发 `_showEntityInfoDialog` 弹窗
- 弹窗用 `CupertinoAlertDialog + SingleChildScrollView + Column`,展示 7 个字段(主体名称/DBA/DUNS/统一社会信用代码/注册地址/行业/邮箱),中英对照,底部说明 Dun & Bradstreet 认证
- 所有标签使用 `AppTypography.caption2`,标签 `w600` 加粗,值正常字重,符合 iOS HIG
#### 2. 任务B:GDPR 欧盟代表改为"暂无代表"
GDPR Art.27 要求非欧盟主体在面向欧盟用户提供服务时须指定 EU 代表。本工作室暂未指定,但原政策声明"已指定欧盟代表"与事实不符,可能被欧盟监管机构质疑。
**修改位置**:
- 网页中文区 12.1 GDPR 子章节 — 「我们已指定欧盟代表,联系方式可通过本政策列明的邮箱获取」改为「我们暂未指定欧盟代表(GDPR Art.27 代表)。如有需要,您可通过本政策列明的邮箱与我们联系」
- 网页英文区 XIII. International Data Protection GDPR 子章节同步表述为「We have not yet designated an EU Representative under GDPR Art.27. If needed, you may contact us via the email listed in this policy.」
- App 内隐私政策简版新增「12. 国际数据保护」章节,欧盟代表表述与网页一致
#### 3. 任务C:网页英文区补全 8 个缺失章节
英文区章节由 10 个补齐至 18 个(Zero-XIV),与中文区 17 个章节基本对齐(英文区多「IX. Deceased Person's Information」死者信息章节,是英文区原有保留项)。
| 新增英文章节 | 行号 | 对应中文章节 |
|---|---|---|
| Zero. Definitions(由 `<p>` 改为 `<h2>`) | 708 | 零、定义 |
| II.5. Business Functions and Personal Information Mapping Table(含 14 行表格) | 805-903 | 二点五、业务功能与个人信息映射表 |
| VI. Cookie and Similar Technologies | 1047 | 六、Cookie 及同类技术 |
| VII. Third-Party SDKs(含 SDK 表格) | 1049 | 七、第三方SDK |
| VII.5. Automated Decision-Making | 1079 | 七点五、自动化决策 |
| VIII.5. Personal Information Security Incident Response | 1105 | 八点五、个人信息安全事件处置 |
| X.5. Personal Information Protection Impact Assessment | 1142 | 九点五、个人信息保护影响评估 |
| XIII. International Data Protection(含 13.1 GDPR/13.2 CCPA/13.3 COPPA) | 1186 | 十二、国际数据保护 |
**章节号顺延**:原 VI→VIII、VII→IX、VIII→X、IX→XI、X→XII、XI→XIV,所有子章节号(6.x→8.x、7.x→9.x、8.x→10.x、11.x→14.x)同步顺延。
**翻译质量**:严格保留 HTML 标签结构(`<h2>`/`<h3>`/`<p>`/`<ul>`/`<li>`/`<table>`/`<span class="highlight">`),表格(`<table><thead><tbody>`)严格保留。翻译符合法律文书风格。
#### 4. 任务D:App 内隐私政策简版补全为详版
App 内 `privacy_policy_page.dart` 章节由 10 章补全至 17 章,与网页详版章节结构对齐(用户选择"补全章节保持简版"方案,不引入表格 widget)。
| 新增章节 | 内容摘要 |
|---|---|
| 0. 定义 | 9 个术语定义(个人信息/敏感信息/处理者/匿名化/去标识化等) |
| 2.5 业务功能与个人信息映射表 | 5 项业务功能 × 个人信息映射(纯文本,网页详版含表格) |
| 7. 第三方 SDK | 当前未集成声明 + 未来集成承诺 |
| 7.5 自动化决策 | 个性化推荐说明 + 退出权 + 禁止用户画像承诺 |
| 8.5 个人信息安全事件处置 | 5 步应急响应(72h 评估/通知/报告/记录 3 年) |
| 9.5 个人信息保护影响评估 | 5 种触发情形 + 评估内容 + 3 年保留 |
| 12. 国际数据保护 | GDPR/CCPA/COPPA 简版,欧盟代表声明为"暂无代表" |
保持现有 `_buildSection + 纯文本` UI 风格,不引入表格 widget。文件头注释按规范更新。**隐私政策正文"最后更新日期"保持 2026年5月7日 不变**(用户要求)。
#### 5. 涉及文件
- `lib/l10n/types/t_about.dart` — 新增 entityInfoMenu/entityInfoMenuDesc 字段(构造/字段/toMap/fromMap 四处)
- `lib/l10n/languages/*.dart` — 14 个语言文件补全 entityInfoMenu/entityInfoMenuDesc 翻译
- `lib/features/profile/presentation/about_page.dart` — _LegalSection 新增主体信息菜单项 + _showEntityInfoDialog 弹窗 + _entityLine 渲染辅助方法
- `lib/features/settings/presentation/privacy/privacy_policy_page.dart` — 章节补全(0/2.5/7/7.5/8.5/9.5/12)+ 文件头更新
- `docs/toolsapi/public/agreements/privacy-policy.html` — 英文区补全 8 章节 + 章节号顺延 + GDPR 欧盟代表表述修正(中英两处)
- `CHANGELOG.md`
#### 6. 验证
- `flutter analyze lib/l10n/languages/ lib/l10n/types/t_about.dart lib/features/profile/presentation/about_page.dart lib/features/settings/presentation/privacy/privacy_policy_page.dart` — No issues found!(8.2s)
- 网页版通过 SFTP 上传至 `https://tools.wktyl.com/agreements/privacy-policy.html`,curl 验证新增章节已上线
#### 7. 不足与建议
- App 内隐私政策简版补全后,表格(如业务功能映射表)用纯文本代替,信息密度低于网页版。如未来需要更丰富展示,可考虑用 `Table` widget 重构
- 网页英文区比中文区多「IX. Deceased Person's Information」章节(英文区原有保留),未来可考虑中文区也补这一章
- 关于页弹窗内容仍为硬编码中英对照,未来如需深度多语言,可提取到 `t_about.dart` 字段
---
***
## [v6.132.0] - 2026-06-26
### ✨ 新增(隐私政策披露 D-U-N-S 编号 + 主体信息章节 — 适配全球上架合规)
#### 背景
工作室已取得邓白氏码D-U-N-S Number: 586261192App 计划在 Apple App Store / Google Play 全球上架。原隐私政策App 内 + 网页版仅披露公司名、统一社会信用代码、ICP 备案缺少国际通用的企业身份标识不利于海外监管机构、合作方与用户进行主体身份核验。GDPR 第 13/14 条要求"数据控制者"身份清晰披露D-U-N-S 作为全球企业唯一标识可显著提升合规性并降低审核争议。
#### 1. App 内隐私政策页privacy_policy_page.dart
- 在「8. 政策更新」与「9. 联系我们」之间新增「9. 主体信息」章节
- 原「9. 联系我们」顺延为「10. 联系我们」
- 「9. 主体信息」披露主体名称中英、经营别称DBA、D-U-N-S 编号、统一社会信用代码、注册地址(中英)、所属行业、隐私事务联系邮箱
- 数据源Dun & Bradstreet 官网 + 工商注册信息
- 文件头注释按规范更新「更新时间 / 上次更新内容」
- **隐私政策正文"最后更新日期"保持 2026年5月7日 不变**(用户要求)
#### 2. 网页版隐私政策privacy-policy.html
- **中文区**:在「十、联系我们」前插入「十、主体信息」;原「十、联系我们」顺延为「十一、联系我们」;原「十一、国际数据保护」顺延为「十二、国际数据保护」,其子章节 11.1/11.2/11.3 同步顺延为 12.1/12.2/12.3
- **英文区**在「IX. Contact Us」前插入「IX. Entity Information」原「IX. Contact Us」顺延为「X. Contact Us」原「X. Legal Application and Dispute Resolution」顺延为「XI. Legal Application and Dispute Resolution」其子章节 10.1/10.2 同步顺延为 11.1/11.2
- **footer**在「统一社会信用代码」与「ICP 备案号」之间新增 D-U-N-S 一行;地址统一更新为 D-U-N-S 官网注册地址中国云南省红河哈尼族彝族自治州弥勒市朋普镇朋普社区朋肖路49号 邮编652301
- **DATA 对象**:新增 `duns` 字段(中英);更新 `contact` 地址为 D-U-N-S 官网注册地址(中英);英文公司名由原"…Weifengbao…"修正为 D-U-N-S 官网登记的"Mile Pengpu Town Micro Storm Network Technology Studio"
- **switchLang 函数**:新增 `footer-duns` 元素的同步更新逻辑
- **联系我们章节的"通信地址"同步更新**为 D-U-N-S 官网注册地址(中英),保证整篇文档地址一致性
#### 3. 关键决策与发现
- **地址不一致问题**:原 footer 与「联系我们」中填写的"云南省昆明市西山区滇池度假区碧鸡街道车家壁513号"与 D-U-N-S 官网登记的注册地址(红河州弥勒市朋普镇)不符。统一社会信用代码 92532526MA6PCX153W 中的 532526 行政区划码对应弥勒市,印证 D-U-N-S 官网地址为工商注册地址。本次以 D-U-N-S 官网地址为准统一全文,避免 Apple/Google 审核时主体信息核对出现偏差
- **法定代表人字段移除**初版主体信息含「法定代表人李振阳Zhenyang Li」字段应要求移除App 内 + 网页中英三处均已删除)。隐私政策主体信息聚焦"组织身份"而非"自然人身份",更符合数据最小化原则
- **英文公司名修正**:原 DATA.en.company 为"Mile City Pengpu Town Weifengbao Network Technology Studio",与 D-U-N-S 官网登记"Mile Pengpu Town Micro Storm Network Technology Studio"不一致。本次以 D-U-N-S 官网为准修正,确保 Apple Developer Program 主体核验通过
#### 4. 合规建议(供后续维护参考)
- **App Store Connect**:在 App 信息 → "开发者信息"处填写 D-U-N-S 586261192确保与隐私政策披露一致
- **Google Play Console**:在"应用内容 → 隐私政策"URL 提交本页面,主体信息保持一致
- **欧盟 GDPR 代表**:当前"国际数据保护"章节声明已指定欧盟代表,但未具体披露代表身份。若目标用户含欧盟且无实体,建议后续在主体信息或国际数据保护章节明确 EU 代表身份与联系方式GDPR Art.27
- **CCPA"请勿出售"入口**:当前声明"不出售个人信息"已合规;若后续商业模式变更需设立专门"请勿出售我的个人信息"链接
- **关于页同步**:建议检查 App 内「关于」页面t_about.dart是否同步展示 D-U-N-S保持多端一致本次未涉及留待后续
#### 涉及文件
- `lib/features/settings/presentation/privacy/privacy_policy_page.dart`新增「9. 主体信息」章节 + 章节顺延 + 文件头更新)
- `docs/toolsapi/public/agreements/privacy-policy.html`(中文区/英文区新增「主体信息」章节 + 章节顺延 + footer 加 D-U-N-S + DATA 加 duns 字段 + 地址统一为 D-U-N-S 官网注册地址 + 英文公司名修正 + switchLang 加 footer-duns 同步)
- `CHANGELOG.md`
---
***
## [v6.131.0] - 2026-06-26
### ✨ 新增(工作台仪表盘全面国际化 + 动态主题/样式 + 交互增强)
#### 背景
工作台模式右栏默认面板 `OverviewDashboard` 存在 23 处硬编码中文(问候语/区块标题/快捷操作标签/空状态/统计单位),不支持多语言;未消费 `AppThemeExtension``fontScale`/`fontWeight` 动态样式;动画 duration 硬编码不响应无障碍 `reduceAnimations` 降级;问候语在 build 内取 `DateTime.now().hour`,跨午时停留不刷新;`favoriteState.total` 等字段缺空指针防护。
#### 1. 新增翻译模块 TDashboard
- 新建 `lib/l10n/types/t_dashboard.dart` — 定义 `TDashboard` 类型27 个字段覆盖:时段问候语(5)、问候区(1)、今日推荐(4)、快捷操作(9)、最近浏览(2)、数据统计(6),含 `toMap`/`fromMap`/`fallback` 回退
- `lib/l10n/types/t_root.dart` — 注入 `dashboard` 字段(构造函数/字段/toMap/fromMap 四处)
- `lib/l10n/types/t.dart` — 导出 `t_dashboard.dart`
- 14 语言文件补全 `dashboard: TDashboard(...)` 段落zh_CN/en 手写zh_TW/ja/ko/de/it/es/fr/pt/ru/ar/bn/hi 12 种补全,`T.withFallback` 自动回退英语
#### 2. 重构 overview_dashboard.dart
- **ConsumerStatefulWidget + Timer**:每分钟校验小时变更,跨午时(12:00)/傍晚(18:00)/深夜(22:00)自动刷新问候语与图标,`dispose` 取消定时器
- **全文案多语言**`ref.watch(translationsProvider)` 接入23 处硬编码替换为 `t.dashboard.xxx`
- **动态主题/样式**:字号统一 `× ext.fontScale` 缩放,正文字重跟随 `ext.fontWeight``GlassContainer` 自动消费 `cardStyleId`/`cornerRadiusId`/`glassBlurMultiplier`
- **触觉反馈**:快捷操作/最近浏览点击加 `HapticFeedback.selectionClick()`
- **骨架屏**:今日推荐加载中(`homeState.isLoading && recommends.isEmpty`)显示 `Shimmer` 占位,深浅色自适应
- **无障碍降级**:响应 `generalSettingsProvider.reduceAnimations`,动画 `duration` 降为 `Duration.zero``AppIcon.animate` 关闭,`SlideAnimation.verticalOffset` 归零
- **空指针防护**`authState.user?.signinDays ?? 0``streakDays` 单位用 `db.streakDayUnit` 拼接i18n
- **作者引用 i18n**`'$db.authorPrefix${sentence.author ?? db.anonymousAuthor}'``author` 空字符串回退佚名
#### 3. 举一反三(附带发现 → 已同步修复)
- **`t_root.dart``toMap()` 缺少 `'quickCard': quickCard.toMap()` 键**(现有 bug导致 `T.withFallback``quickCard` 模块失效——非中文/英语语言的 quickCard 空字段无法回退英语。**本次已补上该键**,并审查 `t_settings_advanced` 的 5 个 impeller 翻译键v6.129.0 新增)的 toMap/fromMap 完整性,确认无遗漏
- **`translation_io_service.dart``importFromJson` 构造 `T(...)` 缺少 `dashboard` 参数**v6.130.0 给 T 加 `required this.dashboard` 后未同步更新,会导致编译错误)。**本次已补上 `dashboard: fallback.dashboard`**,参照 `quickCard` 的处理模式dashboard 属于非用户可编辑导出模块,直接用 fallback
- **设计说明**`tToMap`/`exportAllTranslations` 是"导出用户可编辑翻译"方法,设计上只含 nav/common/profile/settings/note/beta/submit/studyPlan/correction/leisure 9 个核心模块,不含 quickCard/dashboard——这是预期行为无需改动
- **覆盖率检测自动包含**`translation_coverage.dart` 通过 `zhCN.toMap()` 自动遍历所有 sectiontoMap 补全 quickCard 键后,覆盖率报告会自动包含 quickCard 模块的字段统计
#### 涉及文件
- `lib/l10n/types/t_dashboard.dart`(新增)
- `lib/l10n/types/t_root.dart``lib/l10n/types/t.dart`(注入导出 + 修复 toMap 漏写 quickCard 键)
- `lib/l10n/translation_io_service.dart`(修复 importFromJson 的 T 构造缺 dashboard 参数)
- `lib/l10n/languages/*.dart`14 个语言文件补全 dashboard 段落)
- `lib/app/layout/overview_dashboard.dart`(重构)
- `CHANGELOG.md`
---***
## [v6.130.0] - 2026-06-26
### ✨ 新增macOS 权限动态申请 — 替代 permission_handler_apple 无 macOS 实现的问题)
#### 背景macOS 端权限无法动态申请
- **Issue**: `permission_handler_apple 9.4.9` 仅支持 iOS无 macOS 实现。此前 macOS 端权限管理页面直接返回 `granted`,用户点击"请求权限"按钮不会弹出系统授权对话框,实际授权发生在首次访问资源时由系统自动触发(被动模式)。用户无法在权限管理页面主动触发授权。
- **方案**: 新建 `PermissionManager.swift` 原生权限管理器,通过 `apps.xy.xianyan/macos.app` MethodChannel 暴露给 Flutter实现 macOS 原生权限动态申请。
- **支持的权限**:
| 权限 | 原生 API | 状态查询 | 请求方式 |
|---|---|---|---|
| 相机 | AVFoundation | `AVCaptureDevice.authorizationStatus(for: .video)` | `AVCaptureDevice.requestAccess(for: .video)` |
| 麦克风 | AVFoundation | `AVCaptureDevice.authorizationStatus(for: .audio)` | `AVCaptureDevice.requestAccess(for: .audio)` |
| 相册 | Photos framework | `PHPhotoLibrary.authorizationStatus(for:)` | `PHPhotoLibrary.requestAuthorization(for:)` |
| 通知 | UserNotifications | `UNUserNotificationCenter.getNotificationSettings` | `UNUserNotificationCenter.requestAuthorization` |
- **权限状态映射**:
- `notDetermined` → 未决定(可请求)
- `granted` → 已授权(含 `.authorized` / `.limited` / `.provisional` / `.ephemeral`
- `permanentlyDenied` → 已拒绝macOS 拒绝后不再次弹窗,需去系统设置)
- `restricted` → 受限(如家长控制)
- **实现**:
1. 新建 `macos/Runner/PermissionManager.swift` — 原生权限管理器AVFoundation/Photos/UserNotifications
2. `AppDelegate.swift``registerAppChannel` 中新增 3 个 MethodChannel 方法:
- `checkPermission` — 查询权限状态(异步)
- `requestPermission` — 请求权限(触发系统 TCC 弹窗)
- `openPermissionSettings` — 打开系统设置 - 隐私与安全性
3. `macos/Runner.xcodeproj/project.pbxproj` — 将 PermissionManager.swift 添加到 Xcode 项目
4. `MacosPlatformService` 新增 `checkPermission` / `requestPermission` / `openPermissionSettings` 三个方法
5. `PermissionService` 修改 macOS 分支:
- `checkStatus` — 调用原生 `checkPermission` 查询 TCC 状态
- `requestPermission` — 调用原生 `requestPermission` 触发系统弹窗(先显示说明对话框,用户确认后触发)
- `openSettings` — 调用原生 `openPermissionSettings` 跳转系统设置
6. `AppPermission` 枚举新增 `macosPermissionName` getter — 返回 macOS 原生权限名称映射
- **举一反三**:
- macOS TCC 权限模型与 iOS 不同:一旦用户拒绝,再次调用 `requestAccess` 不会弹窗(需用户去系统设置重置)。因此 macOS 上 `denied` 映射为 `permanentlyDenied`
- 通知权限是例外:可以多次请求(`requestAuthorization` 每次都会返回当前状态,但不弹窗)
- `UNUserNotificationCenter.getNotificationSettings` 是异步的,统一所有权限查询为异步回调模式
- **涉及文件**:
- `macos/Runner/PermissionManager.swift`(新增)
- `macos/Runner/AppDelegate.swift` — channel handler 新增 3 个 case
- `macos/Runner.xcodeproj/project.pbxproj` — 添加文件引用
- `lib/core/services/device/macos_platform_service.dart` — 新增权限管理方法
- `lib/core/services/auth/permission_service.dart` — macOS 分支改为调用原生 API
---
## [v6.129.0] - 2026-06-26
### 🐛 修复macOS 渲染引擎 — Impeller 开关不生效 + x86 警告 + Apple Silicon 提示)
#### 问题:通用设置中 Impeller 开关无论打开或关闭,实际渲染引擎始终是 Skia
- **Issue**: 用户在「通用设置 → 高级 → Impeller 渲染引擎」切换开关并重启应用后,实际渲染引擎始终为 Skia开关完全失效
- **根因**: `MainFlutterWindow.swift``awakeFromNib()` 使用 `setenv("FLUTTER_ENGINE_SWITCH_0", ...)` 试图通过环境变量传递 `--enable-impeller`/`--no-enable-impeller` 给 Flutter 引擎,但 **macOS 桌面 embedder (FlutterMacOS) 不读取 `FLUTTER_ENGINE_SWITCH_<N>` 环境变量**(该机制仅适用于 mobile embedder iOS/Android。同时 `Info.plist` 中的 `FLTEnableImpeller` key 已被早期版本移除,引擎无任何信号可读取,只能走 SDK 默认值Skia`currentImpellerEnabled` 缓存的是预期值而非实际值,进一步掩盖了问题,导致 UI 显示与引擎实际状态不符
- **方案**: 改用 `FlutterDartProject` + `commandLineArguments` 方式传递命令行参数,这是 macOS 桌面 embedder 唯一可靠的引擎开关控制方式,命令行参数优先级高于 Info.plist 的 `FLTEnableImpeller` 与 SDK 默认值
- **实现**:
1. `MainFlutterWindow.awakeFromNib()` — 移除 `setenv("FLUTTER_ENGINE_SWITCH_0", ...)`,改为:
```swift
let dartProject = FlutterDartProject()
dartProject.commandLineArguments = [impellerArg]
let flutterViewController = FlutterViewController(project: dartProject)
```
2. 保留 `currentImpellerEnabled` 静态变量缓存当前引擎实际运行状态(修复后此值与引擎实际状态一致)
3. 默认值策略不变Apple Silicon 默认开启 ImpellerIntel Mac 默认关闭Metal 驱动有渲染资源累积 bug
- **举一反三**:
- `FLUTTER_ENGINE_SWITCH_<N>` 环境变量机制仅适用于 mobile embedderiOS/Android桌面 embeddermacOS/Windows/Linux应使用 `FlutterDartProject.commandLineArguments` 或 Info.plist 的 `FLTEnableImpeller` key
- 引擎开关的"实际运行状态"应通过引擎 API 查询,不能直接复用"预期值"作为缓存,否则会掩盖开关不生效的问题
- Info.plist 中移除 `FLTEnableImpeller` 后必须确保有运行时替代方案(命令行参数 / FlutterDartProject否则引擎走 SDK 默认值
- **涉及文件**:
- `macos/Runner/MainFlutterWindow.swift` — `awakeFromNib()` 改用 `FlutterDartProject.commandLineArguments`
- `macos/Runner/AppDelegate.swift` — 注释同步更新(实际逻辑未变)
---
#### 增强x86 端开启 Impeller 前增加警告对话框
- **Issue**: Intel Mac (x86_64) 上 Metal 驱动有渲染资源累积 bug开启 Impeller 可能导致色差/字体割裂/闪烁。用户在不知情情况下开启 Impeller 可能造成体验问题
- **方案**: 在 `_onImpellerToggle(value)` 中检测架构,若为 x86_64 且 `value == true`,先弹出二次确认警告对话框,用户确认后才写入设置;用户取消则恢复开关状态
- **实现**:
1. 新增 `_showX86ImpellerWarningDialog(T t)` 方法 — 返回 `Future<bool>`,用户确认返回 true取消返回 false
2. 对话框包含警告图标(橙色三角形)+ 标题 + 详细描述(说明 Intel Mac 上的风险)+ 取消/仍要开启 两个按钮
3. 用户取消时通过 `setState` 恢复 `_impellerEnabled = false`,不写入 UserDefaults
- **Apple Silicon 提示**: 在重启对话框中新增 arm64 架构专属提示卡片(蓝色背景 + sparkles 图标),说明"Apple Silicon 上 Impeller 基于 Metal 性能更好,推荐开启"
- **新增翻译键**5 个,覆盖全部 14 种语言):
- `impellerX86WarningTitle` — Intel Mac 兼容性警告标题
- `impellerX86WarningDesc` — 警告描述(说明风险 + Apple Silicon 推荐使用 Skia
- `impellerX86WarningConfirm` — 仍要开启
- `impellerX86WarningCancel` — 取消
- `impellerAppleSiliconTip` — Apple Silicon 推荐开启提示
- **涉及文件**:
- `lib/features/settings/presentation/general/general_settings_page.dart` — `_onImpellerToggle` / `_showX86ImpellerWarningDialog` / `_buildImpellerDialogContent`
- `lib/l10n/types/t_settings_advanced.dart` — 新增 5 个字段 + toMap + fromMap
- `lib/l10n/languages/*.dart` — 14 种语言全部添加新键
---
## [v6.128.0] - 2026-06-26
### 🐛 修复macOS 运行时 — permission_handler MissingPluginException + WebRTC 构建冲突)
#### 问题一permission_handler_apple 无 macOS 实现 — 权限管理页点击报错App Store 2.1(a) 直接原因)
- **Issue**: macOS 端权限管理页点击「相册」「麦克风」「相机」请求时抛出 `MissingPluginException(No implementation found for method checkPermissionStatus on channel flutter.baseflow.com/permissions/methods)`,点击「去设置」按钮同样报错。这是 v6.127.0 未覆盖的运行时问题,是 App Store 审核被拒 Guideline 2.1(a) 的直接原因
- **根因**: `permission_handler_apple 9.4.9` 的 `pubspec.yaml` 仅声明 `ios` 平台,**无 macOS 实现**
```yaml
flutter:
plugin:
implements: permission_handler
platforms:
ios: # ⚠️ 仅 ios无 macos
pluginClass: PermissionHandlerPlugin
```
- `permission_handler_apple-9.4.9/` 目录下只有 `ios/`,无 `macos/`
- macOS 端 `GeneratedPluginRegistrant.swift` 不注册 `PermissionHandlerApplePlugin`
- 调用 `Permission.camera.status` / `Permission.photos.request()` / `openAppSettings()` 均抛出 `MissingPluginException`
- **方案**: macOS sandbox 下权限由 **entitlement + Info.plist 用法说明** 自动管理,系统在首次访问受保护资源时弹出授权对话框(由 OS 触发,不由 App 调用)。`permission_service.dart` 添加 macOS 早返回逻辑,跳过 `permission_handler` 调用
- **实现**:
1. `checkStatus()` — macOS 端直接返回 `granted`,避免调用未注册的方法通道
2. `requestPermission()` — macOS 端直接返回 `true`,权限由系统 sandbox + entitlement 自动管理
3. `openSettings()` — 新增 macOS 原生跳转:`Process.run('open', ['x-apple.systempreferences:com.apple.preference.security?Privacy'])`,打开「系统设置 > 隐私与安全性」
4. `_showSettingsDialog` / `_showDeniedDialog` — 将 `openAppSettings()` 调用改为 `openSettings()`,确保 macOS 端「去设置」按钮可用
5. `checkStatus()` / `requestPermission()` 新增 `on MissingPluginException` 兜底Linux 等无 `permission_handler` 实现的桌面端视为已授权
- **举一反三**:
- Flutter 联邦插件federated plugin的平台实现可能只覆盖部分平台使用前需检查 `pub-cache` 中实际包的 `pubspec.yaml` 平台声明
- macOS sandbox 下权限模型与 iOS 不同iOS 需 App 主动调用 `request()`macOS 由 OS 在首次访问时自动弹出entitlement 是「App 级授权」OS 弹框是「用户级授权」
- `MissingPluginException` 不应仅靠 `try-catch` 静默吞掉,需在入口处按平台显式短路,否则权限管理页状态显示为「未决定」具有误导性
- **涉及文件**:
- `lib/core/services/auth/permission_service.dart` — `checkStatus` / `requestPermission` / `openSettings` / `_showSettingsDialog` / `_showDeniedDialog`
---
#### 问题二flutter_webrtc 1.4.0 与 macOS WebRTC-SDK 144.7559.09 版本冲突
- **Issue**: macOS `pod install` 报错 `CocoaPods could not find compatible versions for pod "WebRTC-SDK"``Podfile.lock` 锁定 `144.7559.09`,但 `flutter_webrtc 1.4.0` 的 macOS podspec 依赖 `144.7559.01``.01` 版本)
- **根因**: commit 667f3e49 添加本地 `macos/WebRTC-SDK.podspec.json` 声明 `.09` 版本Intel Mac 渲染修复补丁),但 `flutter_webrtc` 保持在 1.4.0pins `.01`),两者冲突
- **方案**: 升级 `flutter_webrtc` 1.4.0 → 1.5.2(其 macOS podspec 已对齐声明 `s.dependency 'WebRTC-SDK', '144.7559.09'`
- **验证**: `flutter analyze` 通过,`screen_share_page.dart` / `webrtc_service.dart` / `screen_share_provider.dart` 无 API 变更
- **鸿蒙端**: 本地包 `packages/flutter_webrtc`v1.4.0-ohos.1)无法直接升级,待后续同步到 `1.5.2-ohos`;两端 pubspec 独立互不影响
- **涉及文件**:
- `pubspec.yaml` — `flutter_webrtc: ^1.4.0` → `^1.5.2`
- `pubspec.macos.yaml` — 同上
- `pubspec.ohos.yaml` — 更新 flutter_webrtc 注释(远程端已升至 1.5.2
- `pubspec.lock` — flutter_webrtc sha256 更新
- `iOS_macOS_Developer_Guide.md` — 差异对照表更新 + 新增 §2.8.9 flutter_webrtc 特殊包说明 + 新增 §2.8.10 permission_handler_apple macOS 缺失实现说明
---
## [v6.127.0] - 2026-06-26
### 🐛 修复macOS App Store 审核被拒 — Guideline 2.4.5(i) / 2.1(a)
#### macOS 审核三大问题修复 — entitlement 配置 + 权限管理全平台适配
> **注**: 本条目覆盖 entitlement 配置与权限卡片平台过滤;运行时 `MissingPluginException` 修复见 v6.128.0。
- **Issue**: macOS 版本 6.6.25 (2606260) 提交 App Store 被拒,三条审核意见:
1. **Guideline 2.4.5(i)** — 声明了未使用的 entitlement`files.downloads.read-only`、`files.downloads.read-write`、`network.server`
2. **Guideline 2.4.5(i)** — 有功能但缺对应 entitlement相机缺 `com.apple.security.device.camera`,位置缺 `com.apple.security.personal-information.location`
3. **Guideline 2.1(a)** — 在权限管理页点击「相册&存储」「麦克风」请求时显示错误信息
- **根因**:
1. `Release.entitlements` / `DebugProfile.entitlements` 声明了 `files.downloads.*` 但全代码库无 `getDownloadsDirectory` 调用(字体下载用的是 `getApplicationDocumentsDirectory`),属于声明未使用
2. 扫码 / 拍照 / OCR / 语音录制 / 语音转文字功能在共享代码中无平台守卫macOS 端实际可用,但未声明 `device.camera` / `device.audio-input` / `personal-information.photos-library` entitlement且 `Info.plist` 缺 `NSCameraUsageDescription` / `NSMicrophoneUsageDescription` 用法说明
3. `permission_service.dart` 的 `isPlatformRelevant` 对 macOS 无任何过滤鸿蒙端有过滤macOS 落到 `return true`),导致权限管理页在 macOS 展示相机/相册/位置/附近设备/麦克风卡片;点击请求时因 entitlement + Info.plist 双缺,`permission_handler` 在 sandbox 下请求失败弹出错误对话框
4. 位置GPS实际全平台未使用仅 IP 定位 + 手动文本输入),却展示位置权限卡片具有误导性
- **方案**:
1. macOS entitlements移除未用的 `files.downloads.*`;新增 `device.camera` / `device.audio-input` / `personal-information.photos-library`;保留 `network.server`LocalSend 文件传输本地服务器必需)
2. macOS `Info.plist`:新增 `NSCameraUsageDescription` / `NSMicrophoneUsageDescription` 用法说明
3. 重写 `isPlatformRelevant` 为全平台适配macOS/Windows/Linux/Web 各自过滤;位置卡片仅移动端展示(桌面端/Web 只用 IP 定位);附近设备仅移动端;相册仅 macOS + 移动端Windows/Linux/Web 用文件选择器)
4. 新增 `localServer` 虚拟权限卡片(仅桌面端展示),对应 `network.server` entitlement向用户透明声明 LocalSend 文件传输本地服务器能力
- **实现**:
1. **entitlements** — `DebugProfile.entitlements` + `Release.entitlements` 同步修改:移除 2 个 downloads 键,新增 3 个 device/photos 键
2. **Info.plist** — 新增相机 / 麦克风用法说明(中文)
3. **permission_service.dart** — `AppPermission` 枚举新增 `localServer` 虚拟权限;`label/description/usageScenes/denialImpact` 4 个 switch 补全 case`isPlatformRelevant` 重写为「特殊权限单独处理 + 鸿蒙/桌面/Web/移动端分支」结构
4. **l10n** — `t_settings_permission.dart` 新增 `permLocalServer*` 4 字段(构造/字段/toMap/fromMap14 个语言文件新增 4 条翻译
- **举一反三**:
- entitlement 应遵循「最小权限集」原则,声明即需有对应功能,否则触发审核质疑;后续新增 entitlement 必须同步在权限管理页向用户声明用途
- 跨平台权限过滤不能只覆盖单一平台(鸿蒙),所有桌面端 / Web 都需显式过滤,否则同一 bug 会在其他平台复现
- macOS sandbox 下 `permission_handler` 依赖 entitlement + Info.plist usage description 双重配置才能弹系统授权框,缺一即失败
- **审核回复要点**(提交 App Store Connect 时使用):
- `network.server` 用于 LocalSend 局域网文件传输(`HttpServer.bind` / `ServerSocket.bind`),见 `localsend_service.dart` / `tcp_socket_service.dart`
- 已移除未使用的 `files.downloads.*` entitlement
- 已补全相机 / 麦克风 entitlement 与用法说明,权限管理页请求功能正常
- **涉及文件**:
- `macos/Runner/DebugProfile.entitlements` — 移除 downloads新增 camera/audio-input/photos-library
- `macos/Runner/Release.entitlements` — 同上
- `macos/Runner/Info.plist` — 新增 NSCameraUsageDescription / NSMicrophoneUsageDescription
- `lib/core/services/auth/permission_service.dart` — 新增 localServer 枚举 + 全平台 isPlatformRelevant
- `lib/l10n/types/t_settings_permission.dart` — 新增 permLocalServer 4 字段
- `lib/l10n/languages/*.dart`14 个语言文件)— 新增 permLocalServer 4 条翻译
---
## [v6.126.0] - 2026-06-25
### 🐛 修复(键盘事件)