# 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` 验证即可。

构建可分发的 release APK（已绑定 debug 签名，产出可直接安装）：

```bash
export JAVA_HOME="D:/Dev/JDK-17" ANDROID_HOME="D:/Dev/Android/SDK"
./gradlew assembleRelease
# 产物：app/build/outputs/apk/release/app-release.apk
adb install -r app/build/outputs/apk/release/app-release.apk
```

> `release` 复用 `debug` 签名配置（`signingConfigs.getByName("debug")`），适合个人发布到 GitHub Release；正式商店上架前需替换为独立的 release keystore。

## 架构

### 核心数据流

```
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/theme/*` | 固定品牌配色（`Color.kt`）、自定义 `Typography`（`Type.kt`），`A2iTheme` 关闭动态取色、统一深浅色方案（`Theme.kt`）。 |
| `ui/screens/*.kt` | HomeScreen（品牌横幅 + 运行状态面板 + 统计指标）、AppManageScreen、FilterScreen、SettingsScreen、LogScreen。 |
| `ui/Components.kt` | 共享 UI 组件层：`ScreenTitle`、`SectionCard`、`SwitchRow`、`ClickRow`、`IconBox`、`StatusBadge`、`MetricBlock`，以及 `appVersionName(context)`、`drawableToImageBitmap(d)`、`isNotificationListenerEnabled(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` 后续重发。

## 版本与发布

- 当前版本：`versionCode = 3`、`versionName = "1.2.0"`（位于 `app/build.gradle.kts`）。改完代码记得同步递增。
- 远端仓库：`https://github.com/lsxf/a2i.git`，默认分支 `main`。
- 提交策略：`.gitignore` 已排除 `app/build`、`.gradle`、`.kotlin`、`local.properties`、`.claude/`、`do.ps1`、截图与临时文件，**不要**提交它们。
- 发版流程：本地打 tag（`git tag -a v1.2.0 -m "..."`）→ `git push origin v1.2.0` → 在 GitHub Releases 基于 tag 发布，并上传 `app-release.apk` 作为二进制附件。
- UI 现状：统一品牌科技感主题（固定品牌色 + 自定义 Typography + 共享组件层），深浅色双方案，关闭动态取色；新组件统一通过 `ui/Components.kt` 复用，避免各页面各拼一套样式。
