DriftLedger/docs/design/ui-redesign-p7-plan.md
fengmengqi bf04400852 feat: OCR 模型按需下载 + 三层独立开关 + 日志持久化 + 原生浮层主题同步 + 文档重构
OCR 模型按需下载(P8 瘦身):
- 移除 plugins/ppocr/assets/ 内置模型(det 9.8MB + rec 21MB + dict),APK 减包 ~31MB
- 新增 modelDownloader.ts:优先从 HuggingFace/CDN 下载,兜底从 APK assets 拷贝
- OcrModule.kt 新增 setModelDir,支持从 filesystem 加载模型,回退 assets 兼容旧用户
- settingsStore 持久化 ocrModelVersion / ocrModelDir
- 自动化页集成模型状态检查与一键下载 UI

OCR 三层独立控制:
- OcrProcessorConfig 从单一 aiVisionEnabled 拆分为 layer1/2/3 三个独立开关
- 设置页可按层启停(L1 正则规则 / L2 本地 OCR / L3 AI Vision)
- OCR 处理增加耗时与字符数日志

日志系统升级:
- logger.ts 新增 LogFileBackend 抽象,支持磁盘持久化(按日期 app-YYYY-MM-DD.log)
- 新增 logBackend.ts(ExpoLogFileBackend)+ 日志中心页 settings/logs.tsx
- 日志中心:实时缓冲 + 历史文件、4 级过滤、Tag/关键词搜索、JSON 展开、分享导出、7 天过期清理
- _layout.tsx 启动时初始化文件后端 + 日志脱敏(验证码/卡号)

原生浮层 UI 主题同步(P6):
- 新增 floatingUiConfig.ts:JS 侧从 theme tokens + i18n 构建 FloatingUiConfig 推送原生
- 新增 FloatingUiConfigStore.kt:SharedPreferences 存储,三浮层组件读取
- FloatingBillView 重设计:颜色/文案走配置、新增币种 chip、金额校验改 BigDecimal
- FloatingHelper / FloatingTip 同步适配
- _layout.tsx 新增 FloatingUiConfigSyncer,主题/语言切换自动推送

UI 与组件增强:
- FormModal 新增 select/dropdown 控件、行内布局(row/flex)、联动回调 onValuesChange
- 新增 Touchable 通用触摸组件、AccountCreateModal 快速建账弹窗
- 信用卡页展示账单周期/到期还款日/本期应还/剩余可用额度,关联账户改下拉选择
- AI 设置页重做:OpenAI/Gemini/DeepSeek 预设 + 默认 URL/模型
- 引导页新增 Android 权限检查步骤(无障碍/通知/短信/存储/悬浮窗)

去重优化:
- 对手方匹配改为模糊包含(includes),双方均无对手方时判定低置信度重复
- DedupResult 新增 matchedItem 返回匹配对比项

文档重构:
- README.md 重写为入口索引(品牌更新 + 模块概览 + 文档导航表)
- 新增 AGENTS.md(AI 助手贡献指南)、docs/architecture.md(Mermaid 数据流/分层/OCR 级联图)
- 新增 docs/development.md(环境/命令/编码规范/测试/提交规范)、plugins/README.md
- UI 重设计文档(design spec + p1-p8)移入 docs/design/

其他:
- i18n 新增权限/信用卡详情/日志中心/AI 设置等翻译键
- ppocr Config Plugin 修复 import 注入去重;size-optimization 增强
- 新增测试:logger.test.ts、floating-ui-config.test.ts
2026-07-23 19:03:38 +08:00

8.6 KiB
Raw Blame History

P7权限快捷引导 Implementation Plan

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development to implement this plan task-by-task. Steps use checkbox (- [x]) syntax. 不执行任何 git 操作(用户要求,改动留工作区)。不创建额外任务清单。

Goal: 引导页权限步骤从纯文字改可操作状态清单automation 页补齐通知监听/短信/存储三项权限的状态检测与快速跳转按钮。不新增原生权限,只加 UI 入口和一座通知监听状态检测 bridge 方法。


背景决策

  • 不新增权限。所有权限已在 AndroidManifest 中声明(通过 Config PluginP7 只加 UI 入口。
  • 通知监听状态检测需要一个原生 bridge 方法(isNotificationListenerEnabled),因为 RN 侧无法直接检查。用 NotificationManager.getEnabledListenerPackages()
  • 短信 + 存储用 React Native 内置 PermissionsAndroid API。
  • 引导页 UI 沿用现有 Card + Button 组件,与 P1-P5 设计系统一致。

Task 1: 新增桥接方法 isNotificationListenerEnabled

Files:

  • Modify: plugins/accessibility/android/AccessibilityBridgeModule.kt

  • Modify: src/services/accessibilityBridge.ts

  • Step 1: AccessibilityBridgeModule.kt 加方法

@ReactMethod
fun isNotificationListenerEnabled(promise: Promise) {
    try {
        val pm = reactContext.packageManager
        val enabledListeners = android.app.NotificationManager::class.java
            .getMethod("getEnabledListenerPackages")
            .invoke(reactContext.getSystemService(Context.NOTIFICATION_SERVICE)) as? List<String>
        val myPkg = reactContext.packageName
        promise.resolve(enabledListeners?.any { it.startsWith(myPkg) } == true)
    } catch (e: Exception) {
        promise.resolve(false)
    }
}

注意getEnabledListenerPackages() 在 Android 11+ 需 isNotificationListenerEnabled(ComponentName);优先用反射避免 API level lint 错误,反射失败返回 false。

  • Step 2: JS 侧 bridge 接口

NativeAccessibilityBridgeisNotificationListenerEnabled(): Promise<boolean>;

  • Step 3: 编译验证

cd android && ./gradlew :app:compileDebugKotlin → BUILD SUCCESSFUL


Task 2: 引导页权限步骤(_onboarding.tsx 重写 permissions 步骤)

Files:

  • Modify: src/app/_onboarding.tsx

  • Modify: src/i18n/zh.tssrc/i18n/en.ts(少量新键)

  • Step 1: i18n 新键zh/en 对等)

onboarding section 加:

permAccessibility    无障碍服务 / Accessibility Service
permNotification     通知监听 / Notification Listener
permOverlay          悬浮窗 / Overlay
permSms              短信读取 / SMS
permStorage          相册/存储 / Photos & Storage
permGranted          已授权 / Granted
permNotGranted       未授权 / Not Granted
permOpenSettings     前往设置 / Open Settings
permRequest          请求权限 / Request
  • Step 2: 重写 permissions 步骤

替换 _onboarding.tsx:48key: 'permissions' 步骤内容。在当前 description 下方加权限状态清单Card 包裹,每行:图标 + 名称 + 状态点 + 操作按钮)。

权限列表:

  1. 无障碍服务BIND_ACCESSIBILITY_SERVICE)—— 最核心。检测 serviceRunning(从 getAccessibilityBridge().isServiceRunning() 异步取)。未授权 → 跳转 ACCESSIBILITY_SETTINGS
  2. 通知监听BIND_NOTIFICATION_LISTENER_SERVICE)—— 检测 bridge.isNotificationListenerEnabled()。未授权 → 跳转 ACTION_NOTIFICATION_LISTENER_SETTINGS
  3. 短信RECEIVE_SMS)—— PermissionsAndroid.check()。未授权 → PermissionsAndroid.request()
  4. 存储READ_MEDIA_IMAGESAndroid 13+ 用 READ_MEDIA_IMAGES,否则 READ_EXTERNAL_STORAGE)—— 同上

实现细节

  • const [permStates, setPermStates] = useState<Record<string, boolean | null>>({}) —— null = 加载中
  • useEffect(() => { checkAllPermissions(); }, []) —— 步骤切到 permissions 时触发
  • checkAllPermissions:调 PermissionsAndroid.check() 检查短信和存储;调 bridge 检查无障碍和通知监听
  • 每行渲染:Ionicons 图标 + permLabel + 右侧状态文字(绿「已授权」/ 灰「未授权」)+ 未授权时显示操作按钮
  • 「全部已授权」时显示一个大绿色勾 + 提示文字,按钮不显示

权限 API 版本适配

  • PermissionsAndroid.PERMISSIONS 里 Android 13+ 是 READ_MEDIA_IMAGES,低版本用 READ_EXTERNAL_STORAGE

  • 统一用 Platform.Version >= 33 ? 'android.permission.READ_MEDIA_IMAGES' : 'android.permission.READ_EXTERNAL_STORAGE'

  • Step 3: 验证

Run: npm run typecheck && npm test → 全绿 Run: grep -rn "onboarding.perm" src/i18n/zh.ts src/i18n/en.ts → 确认键对等


Task 3: automation 页补齐权限入口

Files:

  • Modify: src/app/automation/index.tsx

  • Modify: src/i18n/zh.tssrc/i18n/en.ts(少量新键)

  • Step 1: i18n 新键

automation section 加:

notificationTitle       通知监听服务 / Notification Listener
notificationEnabled     已启用 / Enabled
notificationDisabled    未启用 / Disabled
notificationOpenSettings 前往系统设置 / Open Settings
smsPermissionTitle      短信读取权限 / SMS Permission
smsPermissionGranted    已授权 / Granted
smsPermissionRequest    请求权限 / Request
storagePermissionTitle  存储权限 / Storage Permission
storagePermissionGranted 已授权 / Granted
storagePermissionRequest 请求权限 / Request
  • Step 2: automation 页加权限卡片

在现有「无障碍服务状态与控制」Card 之后加一个新 Card<Card title={t('automation.otherPermissions')}>

内容三行:

  1. 通知监听:异步 bridge.isNotificationListenerEnabled() → 状态文字 + 未启用时「前往系统设置」按钮(Linking.sendIntent('android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS')
  2. 短信PermissionsAndroid.check('android.permission.RECEIVE_SMS') + PermissionsAndroid.request()
  3. 存储PermissionsAndroid.check(StoragePermission) + PermissionsAndroid.request()

实现模式

const [notifEnabled, setNotifEnabled] = useState(false);
const [smsGranted, setSmsGranted] = useState(false);
const [storageGranted, setStorageGranted] = useState(false);
useEffect(() => {
  // 异步获取各权限状态
  checkNotif(); checkSms(); checkStorage();
}, []);

每行渲染:Ionicons 图标 + 权限名称 + 状态文字 + 未授权时的按钮。

  • Step 3: 验证

Run: npm run typecheck && npm test → 全绿


Task 4: 编译 + 测试 + 终审

  • Step 1: 编译验证

cd android && ./gradlew :app:compileDebugKotlin → BUILD SUCCESSFUL

  • Step 2: 全量测试

npm test → 全绿;npm run typecheck → 0 错误

  • Step 3: 审计
grep -rn "#[0-9A-Fa-f]\{6\}" src/ --include="*.ts" --include="*.tsx" | grep -v "theme/presets.ts\|theme/palette.ts"   # 无输出
grep -rn "fontFamily" src/app/_onboarding.tsx src/app/automation/index.tsx                                               # 无输出
grep -rnE "📊|💰|💸|📝|🔄|🎉|✨|⚠|📌|🌟" src/i18n/ src/app/_onboarding.tsx src/app/automation/index.tsx              # 无输出(预存 i18n fallbackWarn ⚠ 除外)
  • Step 4: 手工走查(需设备)
  1. 清数据 → 启动应用 → 引导页第 5 步(权限)→ 看到 4 个权限状态
  2. 点未授权的无障碍 → 跳系统无障碍设置 → 返回看到状态已变
  3. 点未授权的通知监听 → 跳系统通知设置
  4. 点短信 → 弹出系统权限对话框
  5. 引导页全部完成后 → 进入 automation 页 → 看到新增的权限卡片

终审修订(已实施,以实际代码为准)

终审日期2026-07-22审计 598 测试全绿、typecheck 干净、Kotlin BUILD SUCCESSFUL。

# 级别 问题 处理
无 Critical / Important 发现

偏差说明

  1. onboarding.permOverlay(悬浮窗)未加入引导页——计划 Task 2 决策列为可选项实际未实现。不影响核心功能sideload 自动授予 + 无障碍运行时浮窗走 ACCESSIBILITY_OVERLAY 不依赖此权限)。
  2. T3 中 Platform.Version >= 33 改为 Number(Platform.Version) >= 33——React Native 的 Platform.Version 类型为 string | number,直接比较会 TS 报错。
  3. T1 的 isNotificationListenerEnabled 使用反射而非 enabledListenerPackages 属性——minSdk=24 < API 27反射是兼容方案。