# 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 Plugin），P7 只加 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 加方法**

```kotlin
@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 接口**

`NativeAccessibilityBridge` 加 `isNotificationListenerEnabled(): 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.ts`、`src/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:48` 的 `key: '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_IMAGES`，Android 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.ts`、`src/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()`

**实现模式**：
```tsx
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: 审计**

```bash
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，反射是兼容方案。
