关于页、用户协议与隐私政策:个人开发者最小合规落地

合规工作的特点是:做少了审核打回,做多了纯属自嗨(个人开发者没有法务)。我们按"审核要什么、用户要什么、多了不要"划出一条最小线,落地为设置里的「关于」区 + 两份法律文本 + 隐私清除入口。这篇是完整清单,顺带把背后的鸿蒙机制讲透——合规文案不是孤立的文案,每一处都对应一个系统 API 或配置项。
1. 关于页七要素
「关于」区的内容是枚举出来的七项:产品中文名、版本号、版权行(完整法律主体)、品牌简称、麦克风用途说明、用户协议入口、隐私政策入口(外加第三方致谢)。每一项都对应一条审核或信任逻辑:
-
产品中文名只用「开口练」。代码名(SpeakLab)永远不出现在用户可见处——这条在项目红线里,关于页是最后的防线;
-
版权行用完整登记主体(
© 2026 琼海享时科技工作室(个人独资)),不能用品牌简称替代。审核对"开发者主体"和"应用内声明主体"一致性是会比对的; -
麦克风用途说明逐字复用权限声明。关于页写的用途和
module.json5里权限声明的reason(系统权限弹窗与系统设置页给用户看的理由)语义必须一致——两处说法不一是典型的审核疑点:
/**
* 麦克风用途说明,须与 string.json `sl_mic_reason` 语义一致(逐字复用)。
*/
export const SPEAKLAB_MIC_REASON: string =
'用于将您的语音实时转为文字,以便练习表达并高亮分析口癖词';
-
致谢要诚实:词库来源(大连理工大学情感词汇本体库)、原项目致谢(
基于 expression-trainer(sisi,MIT)理念重构;本应用为独立产品,并非原应用的直接上架版本)。MIT 许可要求保留声明,而"并非原应用的直接上架版本"这句主动划清界限,是避免与原版产品产生应用市场混淆的自我保护。
2. 鸿蒙权限声明机制:为什么"逐字复用"是审核点
展开讲讲上一条里的 reason 到底在哪。鸿蒙 Stage 模型里,用户授权类(user_grant)权限要在 module.json5 里显式声明:
{
"module": {
"requestPermissions": [
{
"name": "ohos.permission.MICROPHONE",
"reason": "$string:sl_mic_reason", // 用户可见的用途说明,走资源索引
"usedScene": { "abilities": ["EntryAbility"], "when": "inuse" }
}
]
}
}
三个字段各有审核含义:name 决定你申请的权限等级(麦克风是 user_grant,必须运行时向用户申请,代码里走 abilityAccessCtrl.createAtManager().requestPermissionsFromUser(...));reason 是字符串资源引用,审核人员和用户都能在系统权限设置页看到它;usedScene 声明使用场景,when: "inuse" 表示仅前台使用期——这和 B12 讲的"foreground 不自动采麦"是同一承诺的两面。
理解了这套机制,"关于页文案逐字复用 reason"的必要性就具体了:审核者会对照系统权限设置里的用途说明、应用内关于页的说法、以及市场后台填的隐私声明三处。三处措辞漂移,轻则要求说明,重则认定"权限用途描述与实际不符"打回。所以我们的做法是把用途文案常量化,让三处引用同一句话,而不是各写各的。另外权限被拒的降级路径(引导去系统设置开启、不闪退不卡死)也是审核实测项,不是可做可不做。
3. 版本号同源:一处真相,门禁比对
关于页显示的版本号来自常量:
/** 与 AppScope/app.json5 versionName 一致;升版时同步改此处 + app.json5。门禁会比对。 */
export const SPEAKLAB_VERSION_NAME: string = '1.0.0';
export const SPEAKLAB_VERSION_CODE: number = 1;
export const SPEAKLAB_VERSION_DISPLAY: string = '1.0.0(1)';
有人问:鸿蒙不是有运行时 API 吗?确实有——bundleManager.getBundleInfoForSelf() 可以拿到 versionName / versionCode,一行代码永远不用手工同步。我们为什么不用?因为"关于页显示的版本"应该是声明出来的契约,而不是"碰巧读到的值":显示层的 bug(读错 flag、格式拼错、异步时序导致首帧为空)应该被测试抓住,而不是静默显示一个错版本或空版本。常量与 AppScope/app.json5 的一致性由门禁 check-p7-about-legal.sh 比对:升版时漏改任何一处,门禁红。B17 的原则再次出现:靠同步纪律的不如靠自动比对。
4. 协议全文:rawfile 应用内展示
用户协议和隐私政策两份全文以纯文本放在 resources/rawfile/(sl_user_agreement.txt / sl_privacy_policy.txt),应用内 Sheet 全文展示,不跳外链。这个选择的审核逻辑:外链协议页面在审核时可能打不开、内容可能与应用内声明不一致、且个人开发者很难保证链接永久有效。打进包里的文本随版本固化,审核看到的就是用户看到的。
技术上 rawfile 放法律文本是门当户对的选择:rawfile 目录下的文件不进资源索引、不参与编译期校验,按原样打进 HAP,正好适合"内容很长、不需要按语言/分辨率限定、不需要被代码按 ID 引用"的纯文本。读取走 getContext(this).resourceManager.getRawFileContent(...)(也有同步版 getRawFileContentSync),这和 B07 词库加载器是同一套 API——词库、法律文本,两类"打包带走的只读资产"共享同一条加载路径。
加载层姿态也与词库同款:I/O 失败返回空串,UI 降级显示"加载失败"提示,不抛业务异常——法律文本加载失败不该 crash,但也不该假装显示了。
隐私政策的内容与前面所有篇目的技术决策一一对应:麦克风只用于实时转写、不存音频(B10)、Key 仅内存+Asset/vault(B15)、历史仅存本地沙箱(B09)、设置可清除。写隐私政策的最佳时机是架构冻结之后——它是技术决策的复述,不是文学创作。 架构里没有的能力(比如"我们不会上传您的语音")写起来才毫不心虚。
5. 隐私与数据入口
合规的最后一块是用户的数据控制权。鸿蒙应用的数据天然住在应用沙箱里(filesDir、Preferences 都在沙箱内,卸载即清),但"卸载即清"不等于合规完成——监管和审核要求的是应用内可操作的删除入口。我们在设置里提供「隐私与数据」区:历史记录清除(删 speaklab_history 目录,B09)、个性化设置清除(Preferences 四字段)、API Key 清除(双清内存+Asset/vault,B15)。每个通道对自己的数据负全责,清除动作有确认对话框和完成反馈。这些入口既是合规要求(用户有权删除其数据),也是前面各篇"通道自治"设计的自然出口——通道自治的架构,让"按通道删除"几乎不用额外设计。
6. 最小合规清单(可抄)
-
关于页:中文名 / 版本(常量+门禁同源)/ 完整主体版权行 / 权限用途(与
module.json5的 reason 逐字一致) -
权限:user_grant 权限声明 reason + usedScene 齐全;运行时申请有拒绝降级路径
-
用户协议 + 隐私政策:rawfile 全文,应用内展示,加载失败有降级
-
第三方致谢:许可要求的声明 + 与上游的关系澄清
-
数据控制入口:历史 / 设置 / 凭据三类清除,有确认有反馈
-
全部文案常量化,门禁比对版本与关键语义
7. 小结
-
合规最小线 = 审核要的 + 用户要的,多了不要;每要素对应一条审核逻辑。
-
权限用途文案与
module.json5reason 逐字复用:审核会对照系统设置、关于页、市场声明三处。 -
版本号常量声明 + 门禁与 app.json5 比对;不用运行时 bundleManager 读,因为显示层 bug 该被测试抓。
-
rawfile 不进资源索引、原样打包,是法律文本的门当户对;隐私政策是技术决策的复述。
-
沙箱"卸载即清"不替代应用内删除入口;按通道清除是通道自治架构的自然出口。
更多推荐


所有评论(0)