# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## 项目概述 **a2i** — 安卓通知转发到 iOS Bark。监听 Android 系统通知(短信、验证码、聊天等),通过 Bark Server 推送到 iOS 设备。iOS 端点击通知可跳转到对应 App(微信、QQ、Telegram)。 ## 环境配置(本机) 环境变量需要在每次 shell 调用前显式导出: ```bash export JAVA_HOME="D:/Dev/JDK-17" export ANDROID_HOME="D:/Dev/Android/SDK" ``` - JDK 17: `D:\Dev\JDK-17`(项目使用 `sourceCompatibility = VERSION_17`) - Android SDK: `D:\Dev\Android\SDK` - Gradle 8.11.1(wrapper 内置) - AGP 8.9.1 / Kotlin 2.1.0 `local.properties` 已在项目根(`sdk.dir=D:\Dev\Android\SDK`),**不要提交**。 ## 构建与安装 ```bash # 编译 debug APK export JAVA_HOME="D:/Dev/JDK-17" ANDROID_HOME="D:/Dev/Android/SDK" ./gradlew assembleDebug # 产物位置 app/build/outputs/apk/debug/app-debug.apk # 安装到已连接的设备 "D:/Dev/Android/SDK/platform-tools/adb.exe" install -r app/build/outputs/apk/debug/app-debug.apk ``` 无 lint / 测试任务配置(项目未启用)。改完代码直接 `assembleDebug` 验证即可。 ## 架构 ### 核心数据流 ``` NotifyListenerService (系统通知入口) → NotificationProcessor.process(sbn) → Decision(message, reason, code) → BarkClient.push(msg) → OkHttp POST → LogStore.addLog(...) 失败 → 加入 pending → RetryWorker (WorkManager, 15min 周期) ``` ### 包结构 `com.a2i.forwarder` | 文件 | 职责 | |---|---| | `A2iApp.kt` | `Application` 单例;持有全局 `appScope` 和三个 Store(`settings`、`appRules`、`logStore`)。Store 都用 `MutableStateFlow`,UI 通过 `collectAsState()` 订阅。 | | `service/NotifyListenerService.kt` | `NotificationListenerService` 实现;每条通知调 `processor.process()`,过滤后推送。 | | `core/NotificationProcessor.kt` | **核心决策点**。包含 LRU 去重缓存(5s)、脱敏检测(`*****` → 回退到 `tickerText`)、系统状态消息过滤、广告过滤、验证码提取、特殊 App 解析。返回 `Decision(message?, reason, appLabel, code)`。 | | `core/CodeExtractor.kt` | 三段正则:关键词+数字、数字+关键词、纯数字 4-8 位。检测到全 `*****` 时直接返回 null。 | | `core/AdFilter.kt` | 内置营销关键词 + 用户自定义关键词(`settings.adKeywords`)。 | | `core/AppParsers.kt` | 微信/QQ/Telegram 的 `clickUrl` 映射 + 重要消息关键词(@我、验证码、通话等)。 | | `core/BarkClient.kt` | OkHttp POST 到 `${server}/push`,超时 10/10/15s。 | | `core/RetryWorker.kt` | WorkManager 周期任务,每 15 分钟重发 `pending` 队列中的失败消息。 | | `core/LogStore.kt` | 最近 300 条日志 + 失败重发队列(≤100)。DataStore 持久化。 | | `core/IconExporter.kt` | 一键导出所有已安装 App 图标到用户选择的 SAF 目录(用于部署到 iOS Bark 图床)。 | | `store/SettingsStore.kt` | Bark 服务器列表、全局开关、广告关键词等。`MutableStateFlow` + DataStore。 | | `store/AppRulesStore.kt` | 黑/白名单模式 + 默认黑名单(系统 App + 三星剪贴板/键盘 + GMS)。 | | `ui/nav/AppNav.kt` | 4 个底部 Tab + 日志页。 | | `ui/screens/*.kt` | HomeScreen(首页统计 + 版本号)、AppManageScreen、FilterScreen、SettingsScreen、LogScreen。 | | `ui/Components.kt` | `appVersionName(context)` 工具函数用于显示版本号。 | ### 重要约定 - **Store 全局单例**:所有 Store 通过 `A2iApp.instance.xxx` 访问。**不要**在 Compose 中创建新的 Store 实例。 - **协程作用域**:跨屏调用用 `app.appScope.launch { ... }`(Application 级别的 `SupervisorJob + Dispatchers.Default`)。 - **过滤流水线**(顺序很重要,在 `NotificationProcessor.process` 中): 1. 全局开关 → 2. ongoing 过滤 → 3. App 黑/白名单 → 4. 空内容/脱敏 → 5. 噪音包名 → 6. 系统状态消息 → 7. 近空内容 → 8. 广告过滤 → **9. 去重检查(5s 窗口)** → 10. 验证码提取 → 11. 特殊 App 解析 → 12. 构造 BarkMessage - **Bark API** 字段映射在 `BarkMessage.kt`(`@SerialName`):`device_key`/`autoCopy`/`isArchive` 用下划线/驼峰别名,其他用蛇形。 - **版本号**:`app/build.gradle.kts` 的 `versionCode`/`versionName`,运行时通过 `appVersionName(context)` 读取,首页底部居中显示 `v{version}`。改完代码记得同步递增。 ### 通知监听授权 监听服务依赖 `Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS` 授权。`HomeScreen` 检测授权状态,未授权时显示跳转按钮。授权是**一次性**的,卸载后需重新授权。 ### 调试日志 `NotifyListenerService.handle()` 每次都会写一条 `ForwardLog` 到 `LogStore`(无论转发/过滤/失败),可在「日志」页查看最近 300 条。失败会自动加入 `pending` 队列,由 `RetryWorker` 后续重发。