feat(infrared): add infrared control, learning, and virtual remote module #9

Merged
KremeCN merged 41 commits from feat/moler-infrared-control into main 2026-08-25 14:51:34 +08:00
Member

概述 / Summary

完整实现红外控制与学习模块(:android:feature:infrared),支持红外设备按键学习、实体发射、Room 本地持久化,以及空调 (AC) / 电视 (TV) 虚拟遥控器面板。

核心变更 / Key Changes

  1. 领域模型与用例 (:domain):
    • 实现按键学习流程:LearnInfraredKey、ManageInfraredKey、InfraredKeyFactory;
    • 增加虚拟遥控器模型与用例:CreateVirtualRemote、LearnVirtualRemoteKey、SendVirtualRemoteAction、AcCodeGenerator 与预置码库 PresetCodeLibrary。
  2. 数据持久化与仓储 (:android:feature:infrared):
    • 引入 Room 数据库 InfraredDatabase 及 DAO,提供 RoomInfraredKeyRepository 与 RoomVirtualRemoteRepository;
    • 支持多配置隔离(ProviderProfileId)、按键状态管理(LEARNING / READY / FAILED)。
  3. UI 与 ViewModel (:android:feature:infrared):
    • InfraredControlScreen:红外学习与按键调试列表;
    • RemotePanelScreen / AcRemotePanel / TvRemotePanel:空调/电视专属可视化遥控面板;
    • InfraredHubScreen:红外中枢与多遥控器管理入口;
    • 在认证后导航栈中将 DeviceListScreen 的红外入口无缝对接到 InfraredHubRoute。
  4. 测试与文档:
    • 补齐用例、Fakes、Repository、ViewModel 以及 Compose UI 自动化测试;
    • 补充红外控制设计规范与真机硬件验收清单。
### 概述 / Summary 完整实现红外控制与学习模块(`:android:feature:infrared`),支持红外设备按键学习、实体发射、Room 本地持久化,以及空调 (AC) / 电视 (TV) 虚拟遥控器面板。 ### 核心变更 / Key Changes 1. **领域模型与用例 (`:domain`)**: - 实现按键学习流程:`LearnInfraredKey`、`ManageInfraredKey`、`InfraredKeyFactory`; - 增加虚拟遥控器模型与用例:`CreateVirtualRemote`、`LearnVirtualRemoteKey`、`SendVirtualRemoteAction`、`AcCodeGenerator` 与预置码库 `PresetCodeLibrary`。 2. **数据持久化与仓储 (`:android:feature:infrared`)**: - 引入 Room 数据库 `InfraredDatabase` 及 DAO,提供 `RoomInfraredKeyRepository` 与 `RoomVirtualRemoteRepository`; - 支持多配置隔离(`ProviderProfileId`)、按键状态管理(`LEARNING` / `READY` / `FAILED`)。 3. **UI 与 ViewModel (`:android:feature:infrared`)**: - `InfraredControlScreen`:红外学习与按键调试列表; - `RemotePanelScreen` / `AcRemotePanel` / `TvRemotePanel`:空调/电视专属可视化遥控面板; - `InfraredHubScreen`:红外中枢与多遥控器管理入口; - 在认证后导航栈中将 `DeviceListScreen` 的红外入口无缝对接到 `InfraredHubRoute`。 4. **测试与文档**: - 补齐用例、Fakes、Repository、ViewModel 以及 Compose UI 自动化测试; - 补充红外控制设计规范与真机硬件验收清单。
pr-agent-bot bot reviewed 2026-08-25 10:34:30 +08:00
pr-agent-bot bot left a comment

Inline comment

Inline comment
@@ -160,5 +160,5 @@
messageFlow.value = RemoteUiMessage.Error("未配置红外设备名,请点击右上角设置配置")
return
}
if (isLearningModeFlow.value) {

加载卡死

启动学习后,activeLearningSlotFlow 只会被赋值,从未在学习成功、失败或关闭学习模式时清空,因此最后操作的按键会一直显示为加载中。与此同时,协调器丢弃了学习结果,Token 失效等错误也无法触发会话失效处理或错误提示。请根据用例结果或仓库中的按键状态清理活动槽位并处理错误。

**加载卡死** 启动学习后,`activeLearningSlotFlow` 只会被赋值,从未在学习成功、失败或关闭学习模式时清空,因此最后操作的按键会一直显示为加载中。与此同时,协调器丢弃了学习结果,Token 失效等错误也无法触发会话失效处理或错误提示。请根据用例结果或仓库中的按键状态清理活动槽位并处理错误。 <!-- pr-agent-dedup: 08cbe6405e17 --> <!-- pr-agent-key-issue-location: a26f8ce229dc -->
pr-agent-bot bot reviewed 2026-08-25 10:34:30 +08:00
pr-agent-bot bot left a comment

Inline comment

Inline comment
@@ -18,5 +18,5 @@
val composeTestRule = createAndroidComposeRule<MainActivity>()
@Test
fun opensInfraredControlScreenFromFoundationScreen() {
composeTestRule.onNodeWithText("Infrared Control").performClick()

测试失败

点击红外控制入口后会进入 infrared_remote 页面,但测试立即查找仅存在于设置页的 infrared_mode_label 和 infrared_device_name_input,因此测试会超时失败。应先点击 remote_settings_button,或改为断言遥控页元素。

**测试失败** 点击红外控制入口后会进入 `infrared_remote` 页面,但测试立即查找仅存在于设置页的 `infrared_mode_label` 和 `infrared_device_name_input`,因此测试会超时失败。应先点击 `remote_settings_button`,或改为断言遥控页元素。 <!-- pr-agent-dedup: 23ad995fc890 --> <!-- pr-agent-key-issue-location: de86fdfa35ce -->
pr-agent-bot bot reviewed 2026-08-25 10:34:30 +08:00
pr-agent-bot bot left a comment

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 5 🔵🔵🔵🔵🔵
🧪 PR contains tests
🔒 No security concerns identified
## PR Reviewer Guide 🔍 Here are some key observations to aid the review process: <table> <tr><td>⏱️&nbsp;<strong>Estimated effort to review</strong>: 5 🔵🔵🔵🔵🔵</td></tr> <tr><td>🧪&nbsp;<strong>PR contains tests</strong></td></tr> <tr><td>🔒&nbsp;<strong>No security concerns identified</strong></td></tr> </table>
KremeCN added 25 commits 2026-08-25 11:57:09 +08:00
KremeCN force-pushed feat/moler-infrared-control from d27e595ad0 to ee42d59879 2026-08-25 11:57:09 +08:00 Compare
pr-agent-bot bot reviewed 2026-08-25 11:59:25 +08:00
pr-agent-bot bot left a comment

Inline comment

Inline comment
@@ -116,5 +116,5 @@
Scaffold(
topBar = {
TopAppBar(
title = { Text(stringResource(R.string.infrared_title)) },

构建失败

此处通过红外功能模块的 R 引用多个字符串,但本 PR 仅在 android/app 模块中添加了对应资源。应用模块依赖功能模块,因此功能模块编译时无法访问应用资源,这些 R.string.infrared_* 引用会无法解析。请将字符串移到该功能模块的 src/main/res,或放入两者共同依赖的资源模块。

**构建失败** 此处通过红外功能模块的 `R` 引用多个字符串,但本 PR 仅在 `android/app` 模块中添加了对应资源。应用模块依赖功能模块,因此功能模块编译时无法访问应用资源,这些 `R.string.infrared_*` 引用会无法解析。请将字符串移到该功能模块的 `src/main/res`,或放入两者共同依赖的资源模块。 <!-- pr-agent-dedup: 9cd9eff60a4d --> <!-- pr-agent-key-issue-location: 49b89a99e9ef -->
pr-agent-bot bot reviewed 2026-08-25 11:59:26 +08:00
pr-agent-bot bot left a comment

Inline comment

Inline comment
@@ -160,5 +160,5 @@
messageFlow.value = RemoteUiMessage.Error("未配置红外设备名,请点击右上角设置配置")
return
}
if (isLearningModeFlow.value) {

状态残留

开始学习时会设置 activeLearningSlotFlow,但该状态之后没有任何清空路径,也未关联学习成功或失败结果。完成一次学习后,对应按键将永久显示加载状态,即使关闭学习模式也不会恢复。请在协调器任务结束时根据结果清空该状态。

**状态残留** 开始学习时会设置 `activeLearningSlotFlow`,但该状态之后没有任何清空路径,也未关联学习成功或失败结果。完成一次学习后,对应按键将永久显示加载状态,即使关闭学习模式也不会恢复。请在协调器任务结束时根据结果清空该状态。 <!-- pr-agent-dedup: 643c68fe6ccb --> <!-- pr-agent-key-issue-location: 4c87bb7dcaf1 -->
pr-agent-bot bot reviewed 2026-08-25 11:59:26 +08:00
pr-agent-bot bot left a comment

Inline comment

Inline comment
@@ -175,5 +175,5 @@
if (key == null || key.status != InfraredKeyStatus.READY) {
messageFlow.value = RemoteUiMessage.Error("按键【${spec.displayName}】尚未学习,请开启右上角学习模式进行录制")
return
}
sendingKeySlotFlow.value = spec.defaultSlot

重复发射

发射过程中没有检查已有的 sendingKeySlotFlow,而遥控器按键在显示加载状态时仍可点击。用户连续点击电源键会并发执行多次红外发射,可能导致设备连续切换两次并回到原状态;其中任一任务先结束还会提前清除另一个任务的加载指示。请在 ViewModel 中阻止重复请求,或按请求分别跟踪并禁用相应按键。

**重复发射** 发射过程中没有检查已有的 `sendingKeySlotFlow`,而遥控器按键在显示加载状态时仍可点击。用户连续点击电源键会并发执行多次红外发射,可能导致设备连续切换两次并回到原状态;其中任一任务先结束还会提前清除另一个任务的加载指示。请在 ViewModel 中阻止重复请求,或按请求分别跟踪并禁用相应按键。 <!-- pr-agent-dedup: fede3506d9e3 --> <!-- pr-agent-key-issue-location: d6cc1c91e5b2 -->
pr-agent-bot bot reviewed 2026-08-25 11:59:26 +08:00
pr-agent-bot bot left a comment

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 5 🔵🔵🔵🔵🔵
🧪 PR contains tests
🔒 No security concerns identified

⚠️ Review coverage: The following files were not included in this review because of the token budget:

  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/runtime/DataStoreInfraredRuntimeModeStore.kt
  • android/feature/infrared/src/androidTest/kotlin/com/flagship/abox/manager/infrared/remote/InfraredRemoteScreenTest.kt
  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/data/local/InfraredMappers.kt
  • absdk-bridge/src/test/kotlin/com/flagship/abox/manager/absdk/bridge/AbsdkSerializationTest.kt
  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/ui/InfraredControlUiState.kt
  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/application/InfraredLearningCoordinator.kt
  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/data/local/InfraredEntities.kt
  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/runtime/InfraredRuntimeMode.kt
  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/data/local/InfraredDatabase.kt
  • android/feature/infrared/schemas/com.flagship.abox.manager.infrared.data.local.InfraredDatabase/1.json
  • android/app/src/main/res/values-zh-rCN/strings.xml
  • android/app/src/main/res/values/strings.xml
  • android/feature/infrared/src/main/AndroidManifest.xml
## PR Reviewer Guide 🔍 Here are some key observations to aid the review process: <table> <tr><td>⏱️&nbsp;<strong>Estimated effort to review</strong>: 5 🔵🔵🔵🔵🔵</td></tr> <tr><td>🧪&nbsp;<strong>PR contains tests</strong></td></tr> <tr><td>🔒&nbsp;<strong>No security concerns identified</strong></td></tr> </table> <hr> ⚠️ **Review coverage:** The following files were not included in this review because of the token budget: - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/runtime/DataStoreInfraredRuntimeModeStore.kt` - `android/feature/infrared/src/androidTest/kotlin/com/flagship/abox/manager/infrared/remote/InfraredRemoteScreenTest.kt` - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/data/local/InfraredMappers.kt` - `absdk-bridge/src/test/kotlin/com/flagship/abox/manager/absdk/bridge/AbsdkSerializationTest.kt` - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/ui/InfraredControlUiState.kt` - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/application/InfraredLearningCoordinator.kt` - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/data/local/InfraredEntities.kt` - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/runtime/InfraredRuntimeMode.kt` - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/data/local/InfraredDatabase.kt` - `android/feature/infrared/schemas/com.flagship.abox.manager.infrared.data.local.InfraredDatabase/1.json` - `android/app/src/main/res/values-zh-rCN/strings.xml` - `android/app/src/main/res/values/strings.xml` - `android/feature/infrared/src/main/AndroidManifest.xml`
KremeCN added 1 commit 2026-08-25 12:05:13 +08:00
pr-agent-bot bot reviewed 2026-08-25 12:08:29 +08:00
pr-agent-bot bot left a comment

Inline comment

Inline comment
@@ -162,5 +162,5 @@
messageFlow.value = RemoteUiMessage.Error("未配置红外设备名,请点击右上角设置配置")
return
}
if (isLearningModeFlow.value) {

无法重学

学习分支不区分槽位是否已有记录,始终调用 learningCoordinator.learn。仓库明确拒绝同一 SDK 槽位的重复绑定,因此用户重新录制 READY 或 FAILED 按键时会失败,尤其 FAILED 按键在此界面没有可用的恢复路径。应根据 learnedSlotMap 对已有失败项调用重试流程,或显式替换现有绑定。

**无法重学** 学习分支不区分槽位是否已有记录,始终调用 `learningCoordinator.learn`。仓库明确拒绝同一 SDK 槽位的重复绑定,因此用户重新录制 READY 或 FAILED 按键时会失败,尤其 FAILED 按键在此界面没有可用的恢复路径。应根据 `learnedSlotMap` 对已有失败项调用重试流程,或显式替换现有绑定。 <!-- pr-agent-dedup: 347060413a4b --> <!-- pr-agent-key-issue-location: 36abc05cf3f2 -->
pr-agent-bot bot reviewed 2026-08-25 12:08:29 +08:00
pr-agent-bot bot left a comment

Inline comment

Inline comment
@@ -145,5 +145,5 @@
fun switchScene(scene: RemoteScene) {
sceneFlow.value = scene
}
fun toggleLearningMode(enabled: Boolean) {

任务未取消

关闭学习模式时仅清空 UI 槽位,没有保存或取消正在运行的学习任务。用户在录制过程中关闭开关后,底层学习仍会继续,而界面允许发送或再次开始学习;若再次学习同一槽位,旧任务的完成回调还可能提前清除新任务的加载状态。应持有并取消当前任务,或在任务真正结束前保持活动状态。

**任务未取消** 关闭学习模式时仅清空 UI 槽位,没有保存或取消正在运行的学习任务。用户在录制过程中关闭开关后,底层学习仍会继续,而界面允许发送或再次开始学习;若再次学习同一槽位,旧任务的完成回调还可能提前清除新任务的加载状态。应持有并取消当前任务,或在任务真正结束前保持活动状态。 <!-- pr-agent-dedup: 67a90f12e304 --> <!-- pr-agent-key-issue-location: f63601c36e3b -->
pr-agent-bot bot reviewed 2026-08-25 12:08:29 +08:00
pr-agent-bot bot left a comment

Inline comment

Inline comment
@@ -45,5 +45,5 @@
override suspend fun getKey(ref: InfraredKeyRef): DomainResult<SavedInfraredKey?> {
return try {
val entity = dao.getKey(ref.profileId.value, ref.keyId.value)
DomainResult.Success(entity?.toDomain())
} catch (throwable: Throwable) {

取消失效

getKey 以及该类其他 suspend CRUD 方法使用 catch (Throwable) 包住 Room 调用,会将协程取消使用的 CancellationException 映射成 DomainError.Unknown。当调用方在数据库操作期间被取消时,调用链可能继续执行后续状态更新或错误提示,而不是及时终止。应重新抛出 CancellationException,或只捕获预期的数据库异常。

**取消失效** `getKey` 以及该类其他 suspend CRUD 方法使用 `catch (Throwable)` 包住 Room 调用,会将协程取消使用的 `CancellationException` 映射成 `DomainError.Unknown`。当调用方在数据库操作期间被取消时,调用链可能继续执行后续状态更新或错误提示,而不是及时终止。应重新抛出 `CancellationException`,或只捕获预期的数据库异常。 <!-- pr-agent-dedup: 98d54557e8b3 --> <!-- pr-agent-key-issue-location: 2f701428f0ac -->
pr-agent-bot bot reviewed 2026-08-25 12:08:30 +08:00
pr-agent-bot bot left a comment

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 5 🔵🔵🔵🔵🔵
🧪 PR contains tests
🔒 No security concerns identified

⚠️ Review coverage: The following files were not included in this review because of the token budget:

  • android/feature/infrared/build.gradle.kts
  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/runtime/DataStoreInfraredRuntimeModeStore.kt
  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/data/local/InfraredMappers.kt
  • absdk-bridge/src/test/kotlin/com/flagship/abox/manager/absdk/bridge/AbsdkSerializationTest.kt
  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/ui/InfraredControlUiState.kt
  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/application/InfraredLearningCoordinator.kt
  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/data/local/InfraredEntities.kt
  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/runtime/InfraredRuntimeProfileResolver.kt
  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/runtime/InfraredRuntimeMode.kt
  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/data/local/InfraredDatabase.kt
  • android/app/build.gradle.kts
  • android/feature/infrared/schemas/com.flagship.abox.manager.infrared.data.local.InfraredDatabase/1.json
  • android/app/src/main/res/values-zh-rCN/strings.xml
  • android/app/src/main/res/values/strings.xml
  • android/feature/infrared/src/main/AndroidManifest.xml
## PR Reviewer Guide 🔍 Here are some key observations to aid the review process: <table> <tr><td>⏱️&nbsp;<strong>Estimated effort to review</strong>: 5 🔵🔵🔵🔵🔵</td></tr> <tr><td>🧪&nbsp;<strong>PR contains tests</strong></td></tr> <tr><td>🔒&nbsp;<strong>No security concerns identified</strong></td></tr> </table> <hr> ⚠️ **Review coverage:** The following files were not included in this review because of the token budget: - `android/feature/infrared/build.gradle.kts` - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/runtime/DataStoreInfraredRuntimeModeStore.kt` - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/data/local/InfraredMappers.kt` - `absdk-bridge/src/test/kotlin/com/flagship/abox/manager/absdk/bridge/AbsdkSerializationTest.kt` - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/ui/InfraredControlUiState.kt` - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/application/InfraredLearningCoordinator.kt` - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/data/local/InfraredEntities.kt` - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/runtime/InfraredRuntimeProfileResolver.kt` - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/runtime/InfraredRuntimeMode.kt` - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/data/local/InfraredDatabase.kt` - `android/app/build.gradle.kts` - `android/feature/infrared/schemas/com.flagship.abox.manager.infrared.data.local.InfraredDatabase/1.json` - `android/app/src/main/res/values-zh-rCN/strings.xml` - `android/app/src/main/res/values/strings.xml` - `android/feature/infrared/src/main/AndroidManifest.xml`
KremeCN added 1 commit 2026-08-25 12:14:23 +08:00
pr-agent-bot bot reviewed 2026-08-25 12:17:33 +08:00
pr-agent-bot bot left a comment

Inline comment

Inline comment
@@ -171,5 +171,5 @@
if (activeLearningSlotFlow.value != null) {
return
}
activeLearningSlotFlow.value = spec.defaultSlot
val existingKey = state.value.learnedSlotMap[spec.defaultSlot]

重学失败

existingKey 只要非空就会调用 learningCoordinator.retry,但该重试路径用于失败按键,而学习模式也允许点击已经处于 READY 状态的按键。用户重新录制已学习按键时会因状态转换不合法而失败;应提供明确的重新学习或覆盖路径,或者禁止点击已学习按键。

**重学失败** `existingKey` 只要非空就会调用 `learningCoordinator.retry`,但该重试路径用于失败按键,而学习模式也允许点击已经处于 `READY` 状态的按键。用户重新录制已学习按键时会因状态转换不合法而失败;应提供明确的重新学习或覆盖路径,或者禁止点击已学习按键。 <!-- pr-agent-dedup: 365d84abc324 --> <!-- pr-agent-key-issue-location: 6a2d13d13315 -->
pr-agent-bot bot reviewed 2026-08-25 12:17:34 +08:00
pr-agent-bot bot left a comment

Inline comment

Inline comment
@@ -183,5 +183,5 @@
sdkKey = spec.defaultSlot,
)
}
activeLearningJob = job
job.invokeOnCompletion {

错误丢失

学习任务的完成回调只清理加载状态,没有读取或传播学习结果,并且 handleDomainError 仅用于发送操作。设备离线或认证失效导致学习失败时,界面不会显示失败原因,认证失效也不会触发 onSessionInvalid;应由协调器返回或发布结果,并将错误映射为对应的 RemoteUiMessage。

**错误丢失** 学习任务的完成回调只清理加载状态,没有读取或传播学习结果,并且 `handleDomainError` 仅用于发送操作。设备离线或认证失效导致学习失败时,界面不会显示失败原因,认证失效也不会触发 `onSessionInvalid`;应由协调器返回或发布结果,并将错误映射为对应的 `RemoteUiMessage`。 <!-- pr-agent-dedup: 16b03ab5c738 --> <!-- pr-agent-key-issue-location: 392b62285515 -->
pr-agent-bot bot reviewed 2026-08-25 12:17:34 +08:00
pr-agent-bot bot left a comment

Inline comment

Inline comment
@@ -107,5 +107,5 @@
topBar = {
TopAppBar(
title = {
Text(
text = "红外遥控器",

本地化缺失

遥控器标题、返回按钮以及该页面的其他提示和场景标签都直接写死为中文。应用运行在非中文区域设置下仍会显示中文,也无法利用本次新增的字符串资源;应统一改用 stringResource。

**本地化缺失** 遥控器标题、返回按钮以及该页面的其他提示和场景标签都直接写死为中文。应用运行在非中文区域设置下仍会显示中文,也无法利用本次新增的字符串资源;应统一改用 `stringResource`。 <!-- pr-agent-dedup: 362d988ef914 --> <!-- pr-agent-key-issue-location: 132ce4939c22 -->
pr-agent-bot bot reviewed 2026-08-25 12:17:34 +08:00
pr-agent-bot bot left a comment

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 5 🔵🔵🔵🔵🔵
🧪 PR contains tests
🔒 No security concerns identified

⚠️ Review coverage: The following files were not included in this review because of the token budget:

  • android/feature/infrared/build.gradle.kts
  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/runtime/DataStoreInfraredRuntimeModeStore.kt
  • android/feature/infrared/src/androidTest/kotlin/com/flagship/abox/manager/infrared/remote/InfraredRemoteScreenTest.kt
  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/data/local/InfraredMappers.kt
  • absdk-bridge/src/test/kotlin/com/flagship/abox/manager/absdk/bridge/AbsdkSerializationTest.kt
  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/ui/InfraredControlUiState.kt
  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/application/InfraredLearningCoordinator.kt
  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/data/local/InfraredEntities.kt
  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/runtime/InfraredRuntimeMode.kt
  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/data/local/InfraredDatabase.kt
  • android/app/build.gradle.kts
  • android/feature/infrared/schemas/com.flagship.abox.manager.infrared.data.local.InfraredDatabase/1.json
  • android/app/src/main/res/values-zh-rCN/strings.xml
  • android/app/src/main/res/values/strings.xml
## PR Reviewer Guide 🔍 Here are some key observations to aid the review process: <table> <tr><td>⏱️&nbsp;<strong>Estimated effort to review</strong>: 5 🔵🔵🔵🔵🔵</td></tr> <tr><td>🧪&nbsp;<strong>PR contains tests</strong></td></tr> <tr><td>🔒&nbsp;<strong>No security concerns identified</strong></td></tr> </table> <hr> ⚠️ **Review coverage:** The following files were not included in this review because of the token budget: - `android/feature/infrared/build.gradle.kts` - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/runtime/DataStoreInfraredRuntimeModeStore.kt` - `android/feature/infrared/src/androidTest/kotlin/com/flagship/abox/manager/infrared/remote/InfraredRemoteScreenTest.kt` - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/data/local/InfraredMappers.kt` - `absdk-bridge/src/test/kotlin/com/flagship/abox/manager/absdk/bridge/AbsdkSerializationTest.kt` - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/ui/InfraredControlUiState.kt` - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/application/InfraredLearningCoordinator.kt` - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/data/local/InfraredEntities.kt` - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/runtime/InfraredRuntimeMode.kt` - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/data/local/InfraredDatabase.kt` - `android/app/build.gradle.kts` - `android/feature/infrared/schemas/com.flagship.abox.manager.infrared.data.local.InfraredDatabase/1.json` - `android/app/src/main/res/values-zh-rCN/strings.xml` - `android/app/src/main/res/values/strings.xml`
KremeCN added 1 commit 2026-08-25 12:20:41 +08:00
pr-agent-bot bot reviewed 2026-08-25 12:23:29 +08:00
pr-agent-bot bot left a comment

Inline comment

Inline comment
@@ -116,5 +116,5 @@
Scaffold(
topBar = {
TopAppBar(
title = { Text(stringResource(R.string.infrared_title)) },

构建失败

该功能源码引用的是 feature 模块的 com.flagship.abox.manager.infrared.R,但本 PR 的 infrared_* 字符串资源仅添加在宿主 app 模块。Android library 无法反向访问宿主 app 的资源,因此这些 R.string.infrared_* 引用无法解析,feature 将无法编译。请将资源放入 feature 的 src/main/res,或放入其依赖的共享资源模块。

**构建失败** 该功能源码引用的是 feature 模块的 `com.flagship.abox.manager.infrared.R`,但本 PR 的 `infrared_*` 字符串资源仅添加在宿主 app 模块。Android library 无法反向访问宿主 app 的资源,因此这些 `R.string.infrared_*` 引用无法解析,feature 将无法编译。请将资源放入 feature 的 `src/main/res`,或放入其依赖的共享资源模块。 <!-- pr-agent-dedup: 7e961c69657f --> <!-- pr-agent-key-issue-location: d7f5a2b91813 -->
pr-agent-bot bot reviewed 2026-08-25 12:23:29 +08:00
pr-agent-bot bot left a comment

Inline comment

Inline comment
@@ -149,5 +149,5 @@
}
fun toggleLearningMode(enabled: Boolean) {
isLearningModeFlow.value = enabled
if (!enabled) {

取消失效

关闭学习模式时取消的只是等待结果的 ViewModel Job,而 learn/retry 返回的任务由独立的应用级 scope 执行;取消 await 不会停止底层学习。用户关闭录制或离开页面后,硬件仍可能继续学习,并且清空 active slot 后重新开启模式可启动另一个并发学习。应提供对 coordinator 底层任务的取消能力,或在任务真正结束前保留忙碌状态并阻止新任务。

**取消失效** 关闭学习模式时取消的只是等待结果的 ViewModel Job,而 `learn`/`retry` 返回的任务由独立的应用级 scope 执行;取消 `await` 不会停止底层学习。用户关闭录制或离开页面后,硬件仍可能继续学习,并且清空 active slot 后重新开启模式可启动另一个并发学习。应提供对 coordinator 底层任务的取消能力,或在任务真正结束前保留忙碌状态并阻止新任务。 <!-- pr-agent-dedup: 42f163fda940 --> <!-- pr-agent-key-issue-location: bc00d351aea6 -->
pr-agent-bot bot reviewed 2026-08-25 12:23:29 +08:00
pr-agent-bot bot left a comment

Inline comment

Inline comment
@@ -101,5 +101,5 @@
errorMessage = errorMessage,
devicesSlot = {
DeviceListScreen(
onInfraredClick = {
navController.navigate("infrared_remote/${profile.id.value}")

路由失败

profile.id.value 被直接拼接到导航路径中。若提供方返回包含 /、?、# 或 % 的 profile ID,该值会被当作路由结构或编码处理,导致目的地匹配失败或 SavedStateHandle 收到错误参数。请在两个红外路由中使用 Uri.encode,或改用类型安全导航传参。

**路由失败** `profile.id.value` 被直接拼接到导航路径中。若提供方返回包含 `/`、`?`、`#` 或 `%` 的 profile ID,该值会被当作路由结构或编码处理,导致目的地匹配失败或 `SavedStateHandle` 收到错误参数。请在两个红外路由中使用 `Uri.encode`,或改用类型安全导航传参。 <!-- pr-agent-dedup: cc48881b23bf --> <!-- pr-agent-key-issue-location: 18ec65a4c571 -->
pr-agent-bot bot reviewed 2026-08-25 12:23:29 +08:00
pr-agent-bot bot left a comment

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 5 🔵🔵🔵🔵🔵
🧪 PR contains tests
🔒 No security concerns identified

⚠️ Review coverage: The following files were not included in this review because of the token budget:

  • android/feature/infrared/build.gradle.kts
  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/runtime/DataStoreInfraredRuntimeModeStore.kt
  • android/feature/infrared/src/androidTest/kotlin/com/flagship/abox/manager/infrared/remote/InfraredRemoteScreenTest.kt
  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/data/local/InfraredMappers.kt
  • absdk-bridge/src/test/kotlin/com/flagship/abox/manager/absdk/bridge/AbsdkSerializationTest.kt
  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/ui/InfraredControlUiState.kt
  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/application/InfraredLearningCoordinator.kt
  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/data/local/InfraredEntities.kt
  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/runtime/InfraredRuntimeProfileResolver.kt
  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/runtime/InfraredRuntimeMode.kt
  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/data/local/InfraredDatabase.kt
  • android/app/build.gradle.kts
  • android/feature/infrared/schemas/com.flagship.abox.manager.infrared.data.local.InfraredDatabase/1.json
  • android/app/src/main/res/values-zh-rCN/strings.xml
  • android/app/src/main/res/values/strings.xml
  • android/feature/infrared/src/main/AndroidManifest.xml
## PR Reviewer Guide 🔍 Here are some key observations to aid the review process: <table> <tr><td>⏱️&nbsp;<strong>Estimated effort to review</strong>: 5 🔵🔵🔵🔵🔵</td></tr> <tr><td>🧪&nbsp;<strong>PR contains tests</strong></td></tr> <tr><td>🔒&nbsp;<strong>No security concerns identified</strong></td></tr> </table> <hr> ⚠️ **Review coverage:** The following files were not included in this review because of the token budget: - `android/feature/infrared/build.gradle.kts` - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/runtime/DataStoreInfraredRuntimeModeStore.kt` - `android/feature/infrared/src/androidTest/kotlin/com/flagship/abox/manager/infrared/remote/InfraredRemoteScreenTest.kt` - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/data/local/InfraredMappers.kt` - `absdk-bridge/src/test/kotlin/com/flagship/abox/manager/absdk/bridge/AbsdkSerializationTest.kt` - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/ui/InfraredControlUiState.kt` - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/application/InfraredLearningCoordinator.kt` - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/data/local/InfraredEntities.kt` - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/runtime/InfraredRuntimeProfileResolver.kt` - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/runtime/InfraredRuntimeMode.kt` - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/data/local/InfraredDatabase.kt` - `android/app/build.gradle.kts` - `android/feature/infrared/schemas/com.flagship.abox.manager.infrared.data.local.InfraredDatabase/1.json` - `android/app/src/main/res/values-zh-rCN/strings.xml` - `android/app/src/main/res/values/strings.xml` - `android/feature/infrared/src/main/AndroidManifest.xml`
KremeCN added 1 commit 2026-08-25 12:28:04 +08:00
pr-agent-bot bot reviewed 2026-08-25 12:33:20 +08:00
pr-agent-bot bot left a comment

Inline comment

Inline comment
@@ -104,5 +104,5 @@
}
}
}
val state: StateFlow<InfraredRemoteUiState> = combine(

状态错配

effectiveProfileAndMode、deviceNameFlow 和 keysFlow 被独立组合,设备名或运行模式切换时可能短暂生成“新配置 + 旧按键列表”的状态。用户此时点击按键,会使用旧按键引用向旧红外设备发送命令,或用新 profile 查询旧按键。应在同一个 flatMapLatest 中生成相互关联的 profile、设备和按键快照,或发送前校验按键的 profile、deviceName 与当前配置一致。

**状态错配** `effectiveProfileAndMode`、`deviceNameFlow` 和 `keysFlow` 被独立组合,设备名或运行模式切换时可能短暂生成“新配置 + 旧按键列表”的状态。用户此时点击按键,会使用旧按键引用向旧红外设备发送命令,或用新 profile 查询旧按键。应在同一个 `flatMapLatest` 中生成相互关联的 profile、设备和按键快照,或发送前校验按键的 profile、deviceName 与当前配置一致。 <!-- pr-agent-dedup: 0c88096cf35a --> <!-- pr-agent-key-issue-location: 6aab6621dcad -->
pr-agent-bot bot reviewed 2026-08-25 12:33:20 +08:00
pr-agent-bot bot left a comment

Inline comment

Inline comment
@@ -221,5 +221,5 @@
activeLearningSlotFlow.value = null
}
}
}
activeLearningJob = job

消息覆盖

“正在录制”消息在启动等待任务之后写入。如果协调器返回已完成的 Deferred,例如 FAKE 模式立即完成,viewModelScope.launch 可能先同步写入成功、失败或 SessionInvalid,随后又被该录制消息覆盖;认证失效时甚至不会触发退出。请在启动任务前设置录制消息。

**消息覆盖** “正在录制”消息在启动等待任务之后写入。如果协调器返回已完成的 `Deferred`,例如 FAKE 模式立即完成,`viewModelScope.launch` 可能先同步写入成功、失败或 `SessionInvalid`,随后又被该录制消息覆盖;认证失效时甚至不会触发退出。请在启动任务前设置录制消息。 <!-- pr-agent-dedup: a62768f2c90c --> <!-- pr-agent-key-issue-location: 4c7d8e5d15f8 -->
pr-agent-bot bot reviewed 2026-08-25 12:33:20 +08:00
pr-agent-bot bot left a comment

Inline comment

Inline comment
@@ -151,5 +151,5 @@
}
fun toggleLearningMode(enabled: Boolean) {
isLearningModeFlow.value = enabled
if (!enabled) {

取消残留

关闭学习模式会取消底层 Deferred 和等待任务,但此路径没有将已持久化的 LEARNING 记录转为 FAILED 或删除,onCleared 也采用相同方式。如果协调器没有在 CancellationException 下以不可取消方式回滚,用户中途关闭开关或离开页面后,该 slot 会保持 LEARNING,后续学习请求又会被直接忽略。请显式完成取消状态收尾,并补充取消学习的持久化测试。

**取消残留** 关闭学习模式会取消底层 `Deferred` 和等待任务,但此路径没有将已持久化的 `LEARNING` 记录转为 `FAILED` 或删除,`onCleared` 也采用相同方式。如果协调器没有在 `CancellationException` 下以不可取消方式回滚,用户中途关闭开关或离开页面后,该 slot 会保持 `LEARNING`,后续学习请求又会被直接忽略。请显式完成取消状态收尾,并补充取消学习的持久化测试。 <!-- pr-agent-dedup: bc52949c626d --> <!-- pr-agent-key-issue-location: 5e714cca24cf -->
pr-agent-bot bot reviewed 2026-08-25 12:33:21 +08:00
pr-agent-bot bot left a comment

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 5 🔵🔵🔵🔵🔵
🧪 PR contains tests
🔒 No security concerns identified

⚠️ Review coverage: The following files were not included in this review because of the token budget:

  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/remote/RemoteModels.kt
  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/runtime/DataStoreInfraredRuntimeModeStore.kt
  • android/feature/infrared/src/androidTest/kotlin/com/flagship/abox/manager/infrared/remote/InfraredRemoteScreenTest.kt
  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/data/local/InfraredMappers.kt
  • absdk-bridge/src/test/kotlin/com/flagship/abox/manager/absdk/bridge/AbsdkSerializationTest.kt
  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/ui/InfraredControlUiState.kt
  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/application/InfraredLearningCoordinator.kt
  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/data/local/InfraredEntities.kt
  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/runtime/InfraredRuntimeProfileResolver.kt
  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/runtime/InfraredRuntimeMode.kt
  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/data/local/InfraredDatabase.kt
  • android/app/build.gradle.kts
  • android/feature/infrared/schemas/com.flagship.abox.manager.infrared.data.local.InfraredDatabase/1.json
  • android/app/src/main/res/values-zh-rCN/strings.xml
  • android/app/src/main/res/values/strings.xml
  • android/feature/infrared/src/main/AndroidManifest.xml
## PR Reviewer Guide 🔍 Here are some key observations to aid the review process: <table> <tr><td>⏱️&nbsp;<strong>Estimated effort to review</strong>: 5 🔵🔵🔵🔵🔵</td></tr> <tr><td>🧪&nbsp;<strong>PR contains tests</strong></td></tr> <tr><td>🔒&nbsp;<strong>No security concerns identified</strong></td></tr> </table> <hr> ⚠️ **Review coverage:** The following files were not included in this review because of the token budget: - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/remote/RemoteModels.kt` - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/runtime/DataStoreInfraredRuntimeModeStore.kt` - `android/feature/infrared/src/androidTest/kotlin/com/flagship/abox/manager/infrared/remote/InfraredRemoteScreenTest.kt` - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/data/local/InfraredMappers.kt` - `absdk-bridge/src/test/kotlin/com/flagship/abox/manager/absdk/bridge/AbsdkSerializationTest.kt` - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/ui/InfraredControlUiState.kt` - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/application/InfraredLearningCoordinator.kt` - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/data/local/InfraredEntities.kt` - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/runtime/InfraredRuntimeProfileResolver.kt` - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/runtime/InfraredRuntimeMode.kt` - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/data/local/InfraredDatabase.kt` - `android/app/build.gradle.kts` - `android/feature/infrared/schemas/com.flagship.abox.manager.infrared.data.local.InfraredDatabase/1.json` - `android/app/src/main/res/values-zh-rCN/strings.xml` - `android/app/src/main/res/values/strings.xml` - `android/feature/infrared/src/main/AndroidManifest.xml`
KremeCN added 1 commit 2026-08-25 12:36:29 +08:00
pr-agent-bot bot reviewed 2026-08-25 12:39:23 +08:00
pr-agent-bot bot left a comment

Inline comment

Inline comment
@@ -247,5 +247,5 @@
}
}
}
override fun onCleared() {

状态残留

用户在录制过程中返回并销毁页面时,这里只取消协程,没有像关闭学习模式时那样调用 failInterruptedLearning。已写入数据库的按键可能永久停留在 LEARNING 状态;再次进入页面后,点击该槽位会因检测到正在学习而直接返回,导致无法重试或重新录制。应在页面销毁时可靠地将被取消的学习记录转为失败状态,或由协调器在取消时完成状态收尾。

**状态残留** 用户在录制过程中返回并销毁页面时,这里只取消协程,没有像关闭学习模式时那样调用 `failInterruptedLearning`。已写入数据库的按键可能永久停留在 `LEARNING` 状态;再次进入页面后,点击该槽位会因检测到正在学习而直接返回,导致无法重试或重新录制。应在页面销毁时可靠地将被取消的学习记录转为失败状态,或由协调器在取消时完成状态收尾。 <!-- pr-agent-dedup: 962aa4239650 --> <!-- pr-agent-key-issue-location: 6712a95bba82 -->
pr-agent-bot bot reviewed 2026-08-25 12:39:24 +08:00
pr-agent-bot bot left a comment

Inline comment

Inline comment
@@ -171,5 +171,5 @@
if (isLearningModeFlow.value) {
if (underlyingLearningDeferred?.isActive == true || activeLearningJob?.isActive == true || activeLearningSlotFlow.value != null) {
return
}
val existingKey = state.value.learnedSlotMap[spec.defaultSlot]

无法重录

只要槽位已存在就调用 learningCoordinator.retry,其中也包括状态为 READY 的已学习按键。但重试流程用于失败按键,设置页也只对 FAILED 状态提供重试,因此用户在学习模式下点击已学习按键以更新红外码时会被状态校验拒绝。应仅对失败按键调用重试,并为已就绪按键提供明确的重新学习或覆盖流程。

**无法重录** 只要槽位已存在就调用 `learningCoordinator.retry`,其中也包括状态为 `READY` 的已学习按键。但重试流程用于失败按键,设置页也只对 `FAILED` 状态提供重试,因此用户在学习模式下点击已学习按键以更新红外码时会被状态校验拒绝。应仅对失败按键调用重试,并为已就绪按键提供明确的重新学习或覆盖流程。 <!-- pr-agent-dedup: 6de2ba8f0bdf --> <!-- pr-agent-key-issue-location: b2730b9541cd -->
pr-agent-bot bot reviewed 2026-08-25 12:39:24 +08:00
pr-agent-bot bot left a comment

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 5 🔵🔵🔵🔵🔵
🧪 PR contains tests
🔒 No security concerns identified

⚠️ Review coverage: The following files were not included in this review because of the token budget:

  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/remote/RemoteModels.kt
  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/runtime/DataStoreInfraredRuntimeModeStore.kt
  • android/feature/infrared/src/androidTest/kotlin/com/flagship/abox/manager/infrared/remote/InfraredRemoteScreenTest.kt
  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/data/local/InfraredMappers.kt
  • absdk-bridge/src/test/kotlin/com/flagship/abox/manager/absdk/bridge/AbsdkSerializationTest.kt
  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/ui/InfraredControlUiState.kt
  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/application/InfraredLearningCoordinator.kt
  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/data/local/InfraredEntities.kt
  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/runtime/InfraredRuntimeProfileResolver.kt
  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/runtime/InfraredRuntimeMode.kt
  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/data/local/InfraredDatabase.kt
  • android/app/build.gradle.kts
  • android/feature/infrared/schemas/com.flagship.abox.manager.infrared.data.local.InfraredDatabase/1.json
  • android/app/src/main/res/values-zh-rCN/strings.xml
  • android/app/src/main/res/values/strings.xml
  • android/feature/infrared/src/main/AndroidManifest.xml
## PR Reviewer Guide 🔍 Here are some key observations to aid the review process: <table> <tr><td>⏱️&nbsp;<strong>Estimated effort to review</strong>: 5 🔵🔵🔵🔵🔵</td></tr> <tr><td>🧪&nbsp;<strong>PR contains tests</strong></td></tr> <tr><td>🔒&nbsp;<strong>No security concerns identified</strong></td></tr> </table> <hr> ⚠️ **Review coverage:** The following files were not included in this review because of the token budget: - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/remote/RemoteModels.kt` - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/runtime/DataStoreInfraredRuntimeModeStore.kt` - `android/feature/infrared/src/androidTest/kotlin/com/flagship/abox/manager/infrared/remote/InfraredRemoteScreenTest.kt` - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/data/local/InfraredMappers.kt` - `absdk-bridge/src/test/kotlin/com/flagship/abox/manager/absdk/bridge/AbsdkSerializationTest.kt` - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/ui/InfraredControlUiState.kt` - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/application/InfraredLearningCoordinator.kt` - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/data/local/InfraredEntities.kt` - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/runtime/InfraredRuntimeProfileResolver.kt` - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/runtime/InfraredRuntimeMode.kt` - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/data/local/InfraredDatabase.kt` - `android/app/build.gradle.kts` - `android/feature/infrared/schemas/com.flagship.abox.manager.infrared.data.local.InfraredDatabase/1.json` - `android/app/src/main/res/values-zh-rCN/strings.xml` - `android/app/src/main/res/values/strings.xml` - `android/feature/infrared/src/main/AndroidManifest.xml`
KremeCN added 1 commit 2026-08-25 12:44:17 +08:00
pr-agent-bot bot reviewed 2026-08-25 12:47:29 +08:00
pr-agent-bot bot left a comment

Inline comment

Inline comment
@@ -74,5 +74,5 @@
private var activeLearningJob: Job? = null
private var underlyingLearningDeferred: kotlinx.coroutines.Deferred<*>? = null
init {
authenticatedProfileId?.let { profile ->

恢复错位

failInterruptedLearning 使用原始认证配置,而按键数据使用经运行模式解析后的 effectiveProfileId。在 FAKE 模式下,按键通常存储于派生配置(如 abox:infrared-fake);进程中断后,这些 LEARNING 记录不会被标记为失败,而 onButtonClick 又会直接忽略已有的 LEARNING 按键,导致该按键无法重试或重新学习。应针对当前运行模式解析出的配置执行恢复。

**恢复错位** `failInterruptedLearning` 使用原始认证配置,而按键数据使用经运行模式解析后的 `effectiveProfileId`。在 FAKE 模式下,按键通常存储于派生配置(如 `abox:infrared-fake`);进程中断后,这些 `LEARNING` 记录不会被标记为失败,而 `onButtonClick` 又会直接忽略已有的 `LEARNING` 按键,导致该按键无法重试或重新学习。应针对当前运行模式解析出的配置执行恢复。 <!-- pr-agent-dedup: fe03f4d5ccbe --> <!-- pr-agent-key-issue-location: 97afcff45f2a -->
pr-agent-bot bot reviewed 2026-08-25 12:47:29 +08:00
pr-agent-bot bot left a comment

Inline comment

Inline comment
@@ -92,5 +92,5 @@
val profile = authenticatedProfileId?.let { profileResolver.resolve(it, mode) }
if (profile == null) {
flowOf(InfraredRemoteConfigSnapshot(effectiveProfileId = null, runtimeMode = mode))
} else {
deviceConfigurationRepository.observeDeviceName(profile).flatMapLatest { deviceResult ->

错误吞没

设备配置和按键观察流中的 DomainResult.Failure 分别被转换为“未配置设备”和空按键列表。数据库读取失败时,界面会错误地提示用户配置设备或重新学习按键,而不是报告实际存储错误。应将失败状态传播到 UI,避免把读取故障当成真实的空数据。

**错误吞没** 设备配置和按键观察流中的 `DomainResult.Failure` 分别被转换为“未配置设备”和空按键列表。数据库读取失败时,界面会错误地提示用户配置设备或重新学习按键,而不是报告实际存储错误。应将失败状态传播到 UI,避免把读取故障当成真实的空数据。 <!-- pr-agent-dedup: f7ef39caa7e5 --> <!-- pr-agent-key-issue-location: babb0d2f9517 -->
pr-agent-bot bot reviewed 2026-08-25 12:47:29 +08:00
pr-agent-bot bot left a comment

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 4 🔵🔵🔵🔵⚪
🧪 PR contains tests
🔒 No security concerns identified

⚠️ Review coverage: The following files were not included in this review because of the token budget:

  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/remote/RemoteModels.kt
  • android/feature/infrared/build.gradle.kts
  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/application/InfraredLearningCoordinator.kt
  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/runtime/DataStoreInfraredRuntimeModeStore.kt
  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/data/local/InfraredMappers.kt
  • absdk-bridge/src/test/kotlin/com/flagship/abox/manager/absdk/bridge/AbsdkSerializationTest.kt
  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/ui/InfraredControlUiState.kt
  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/data/local/InfraredEntities.kt
  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/runtime/InfraredRuntimeProfileResolver.kt
  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/runtime/InfraredRuntimeMode.kt
  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/data/local/InfraredDatabase.kt
  • android/app/build.gradle.kts
  • android/feature/infrared/schemas/com.flagship.abox.manager.infrared.data.local.InfraredDatabase/1.json
  • android/app/src/main/res/values-zh-rCN/strings.xml
  • android/app/src/main/res/values/strings.xml
  • android/feature/infrared/src/main/AndroidManifest.xml
## PR Reviewer Guide 🔍 Here are some key observations to aid the review process: <table> <tr><td>⏱️&nbsp;<strong>Estimated effort to review</strong>: 4 🔵🔵🔵🔵⚪</td></tr> <tr><td>🧪&nbsp;<strong>PR contains tests</strong></td></tr> <tr><td>🔒&nbsp;<strong>No security concerns identified</strong></td></tr> </table> <hr> ⚠️ **Review coverage:** The following files were not included in this review because of the token budget: - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/remote/RemoteModels.kt` - `android/feature/infrared/build.gradle.kts` - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/application/InfraredLearningCoordinator.kt` - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/runtime/DataStoreInfraredRuntimeModeStore.kt` - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/data/local/InfraredMappers.kt` - `absdk-bridge/src/test/kotlin/com/flagship/abox/manager/absdk/bridge/AbsdkSerializationTest.kt` - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/ui/InfraredControlUiState.kt` - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/data/local/InfraredEntities.kt` - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/runtime/InfraredRuntimeProfileResolver.kt` - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/runtime/InfraredRuntimeMode.kt` - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/data/local/InfraredDatabase.kt` - `android/app/build.gradle.kts` - `android/feature/infrared/schemas/com.flagship.abox.manager.infrared.data.local.InfraredDatabase/1.json` - `android/app/src/main/res/values-zh-rCN/strings.xml` - `android/app/src/main/res/values/strings.xml` - `android/feature/infrared/src/main/AndroidManifest.xml`
KremeCN added 1 commit 2026-08-25 12:51:57 +08:00
pr-agent-bot bot reviewed 2026-08-25 12:58:11 +08:00
pr-agent-bot bot left a comment

Inline comment

Inline comment
@@ -116,5 +116,5 @@
Scaffold(
topBar = {
TopAppBar(
title = { Text(stringResource(R.string.infrared_title)) },

构建失败

该库模块引用自身的 com.flagship.abox.manager.infrared.R.string,但本 PR 只在 android/app 的资源目录中添加或修改了这些红外字符串。消费方 app 的资源不会进入库模块的编译类路径,因此 infrared_title 等资源符号无法解析。请将这些字符串移至 android/feature/infrared/src/main/res。

**构建失败** 该库模块引用自身的 `com.flagship.abox.manager.infrared.R.string`,但本 PR 只在 `android/app` 的资源目录中添加或修改了这些红外字符串。消费方 app 的资源不会进入库模块的编译类路径,因此 `infrared_title` 等资源符号无法解析。请将这些字符串移至 `android/feature/infrared/src/main/res`。 <!-- pr-agent-dedup: 06cb602884ea --> <!-- pr-agent-key-issue-location: 5133200eafc7 -->
pr-agent-bot bot reviewed 2026-08-25 12:58:12 +08:00
pr-agent-bot bot left a comment

Inline comment

Inline comment
@@ -183,5 +183,5 @@
}
fun toggleLearningMode(enabled: Boolean) {
isLearningModeFlow.value = enabled
if (!enabled) {

竞态条件

关闭学习模式时会异步触发按 profile 清理全部 LEARNING 记录,但界面立即允许重新开启学习。若用户快速重新开启并开始新任务,尚未执行的清理可能把新任务也标记为 FAILED,导致随后成功的学习无法转换为 READY;初始化和 onCleared 中也存在同类风险。应等待清理完成后再允许新任务,或使用时间戳/任务标识仅清理旧记录。

**竞态条件** 关闭学习模式时会异步触发按 profile 清理全部 `LEARNING` 记录,但界面立即允许重新开启学习。若用户快速重新开启并开始新任务,尚未执行的清理可能把新任务也标记为 `FAILED`,导致随后成功的学习无法转换为 `READY`;初始化和 `onCleared` 中也存在同类风险。应等待清理完成后再允许新任务,或使用时间戳/任务标识仅清理旧记录。 <!-- pr-agent-dedup: 943572f8ab5b --> <!-- pr-agent-key-issue-location: e2acde597e7e -->
pr-agent-bot bot reviewed 2026-08-25 12:58:12 +08:00
pr-agent-bot bot left a comment

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 5 🔵🔵🔵🔵🔵
🧪 PR contains tests
🔒 No security concerns identified

⚠️ Review coverage: The following files were not included in this review because of the token budget:

  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/remote/RemoteModels.kt
  • android/feature/infrared/build.gradle.kts
  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/application/InfraredLearningCoordinator.kt
  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/runtime/DataStoreInfraredRuntimeModeStore.kt
  • android/feature/infrared/src/androidTest/kotlin/com/flagship/abox/manager/infrared/remote/InfraredRemoteScreenTest.kt
  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/data/local/InfraredMappers.kt
  • absdk-bridge/src/test/kotlin/com/flagship/abox/manager/absdk/bridge/AbsdkSerializationTest.kt
  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/ui/InfraredControlUiState.kt
  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/data/local/InfraredEntities.kt
  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/runtime/InfraredRuntimeProfileResolver.kt
  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/runtime/InfraredRuntimeMode.kt
  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/data/local/InfraredDatabase.kt
  • android/feature/infrared/schemas/com.flagship.abox.manager.infrared.data.local.InfraredDatabase/1.json
  • android/app/src/main/res/values-zh-rCN/strings.xml
  • android/app/src/main/res/values/strings.xml
  • android/feature/infrared/src/main/AndroidManifest.xml
## PR Reviewer Guide 🔍 Here are some key observations to aid the review process: <table> <tr><td>⏱️&nbsp;<strong>Estimated effort to review</strong>: 5 🔵🔵🔵🔵🔵</td></tr> <tr><td>🧪&nbsp;<strong>PR contains tests</strong></td></tr> <tr><td>🔒&nbsp;<strong>No security concerns identified</strong></td></tr> </table> <hr> ⚠️ **Review coverage:** The following files were not included in this review because of the token budget: - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/remote/RemoteModels.kt` - `android/feature/infrared/build.gradle.kts` - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/application/InfraredLearningCoordinator.kt` - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/runtime/DataStoreInfraredRuntimeModeStore.kt` - `android/feature/infrared/src/androidTest/kotlin/com/flagship/abox/manager/infrared/remote/InfraredRemoteScreenTest.kt` - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/data/local/InfraredMappers.kt` - `absdk-bridge/src/test/kotlin/com/flagship/abox/manager/absdk/bridge/AbsdkSerializationTest.kt` - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/ui/InfraredControlUiState.kt` - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/data/local/InfraredEntities.kt` - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/runtime/InfraredRuntimeProfileResolver.kt` - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/runtime/InfraredRuntimeMode.kt` - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/data/local/InfraredDatabase.kt` - `android/feature/infrared/schemas/com.flagship.abox.manager.infrared.data.local.InfraredDatabase/1.json` - `android/app/src/main/res/values-zh-rCN/strings.xml` - `android/app/src/main/res/values/strings.xml` - `android/feature/infrared/src/main/AndroidManifest.xml`
KremeCN added 1 commit 2026-08-25 13:07:57 +08:00
pr-agent-bot bot reviewed 2026-08-25 13:12:00 +08:00
pr-agent-bot bot left a comment

Inline comment

Inline comment
@@ -38,5 +38,5 @@
fun relearn(ref: InfraredKeyRef): Deferred<DomainResult<Unit>> = externalScope.async {
relearnInfraredKey(ref)
}
fun failInterruptedLearning(

竞态条件

failInterruptedLearning 启动脱离调用者的协程后立即返回。关闭学习模式后,用户可以马上重新开启并对同一旧按键执行 relearn;尚未完成的清理仍可能按旧按键的创建时间命中该记录,在新学习开始后将其改为 FAILED。建议返回并等待清理任务,或在清理完成前通过互斥和状态控制阻止新的学习操作。

**竞态条件** `failInterruptedLearning` 启动脱离调用者的协程后立即返回。关闭学习模式后,用户可以马上重新开启并对同一旧按键执行 `relearn`;尚未完成的清理仍可能按旧按键的创建时间命中该记录,在新学习开始后将其改为 `FAILED`。建议返回并等待清理任务,或在清理完成前通过互斥和状态控制阻止新的学习操作。 <!-- pr-agent-dedup: 8b20f5d5f95c --> <!-- pr-agent-key-issue-location: 6978a0e3566e -->
pr-agent-bot bot reviewed 2026-08-25 13:12:00 +08:00
pr-agent-bot bot left a comment

Inline comment

Inline comment
@@ -195,5 +195,5 @@
state.value.effectiveProfileId?.let { profile ->
learningCoordinator.failInterruptedLearning(profile, cutoffEpochMillis = shutdownCutoff)
}
} else {
messageFlow.value = RemoteUiMessage.Info("🔴 学习录制模式已开启:点击任意按键即可开始录制")

本地化缺失

多条直接展示给用户的学习状态和错误提示被硬编码为中文,而该功能的其他界面文案支持按 locale 切换。英语等非中文环境执行开启学习、缺少设备或录制按键等流程时仍会显示中文。建议通过资源标识或可本地化的 UI 文本模型传递这些消息;遥控面板中的硬编码标签也应一并迁移。

**本地化缺失** 多条直接展示给用户的学习状态和错误提示被硬编码为中文,而该功能的其他界面文案支持按 locale 切换。英语等非中文环境执行开启学习、缺少设备或录制按键等流程时仍会显示中文。建议通过资源标识或可本地化的 UI 文本模型传递这些消息;遥控面板中的硬编码标签也应一并迁移。 <!-- pr-agent-dedup: 984743d900d6 --> <!-- pr-agent-key-issue-location: ee423d2e68cf -->
pr-agent-bot bot reviewed 2026-08-25 13:12:00 +08:00
pr-agent-bot bot left a comment

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 5 🔵🔵🔵🔵🔵
🧪 PR contains tests
🔒 No security concerns identified

⚠️ Review coverage: The following files were not included in this review because of the token budget:

  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/data/local/InfraredDao.kt
  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/remote/RemoteModels.kt
  • android/feature/infrared/build.gradle.kts
  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/runtime/DataStoreInfraredRuntimeModeStore.kt
  • android/feature/infrared/src/androidTest/kotlin/com/flagship/abox/manager/infrared/remote/InfraredRemoteScreenTest.kt
  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/data/local/InfraredMappers.kt
  • absdk-bridge/src/test/kotlin/com/flagship/abox/manager/absdk/bridge/AbsdkSerializationTest.kt
  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/ui/InfraredControlUiState.kt
  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/data/local/InfraredEntities.kt
  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/runtime/InfraredRuntimeProfileResolver.kt
  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/runtime/InfraredRuntimeMode.kt
  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/data/local/InfraredDatabase.kt
  • android/app/build.gradle.kts
  • android/feature/infrared/schemas/com.flagship.abox.manager.infrared.data.local.InfraredDatabase/1.json
  • android/app/src/main/res/values-zh-rCN/strings.xml
  • android/app/src/main/res/values/strings.xml
  • android/feature/infrared/src/main/AndroidManifest.xml
## PR Reviewer Guide 🔍 Here are some key observations to aid the review process: <table> <tr><td>⏱️&nbsp;<strong>Estimated effort to review</strong>: 5 🔵🔵🔵🔵🔵</td></tr> <tr><td>🧪&nbsp;<strong>PR contains tests</strong></td></tr> <tr><td>🔒&nbsp;<strong>No security concerns identified</strong></td></tr> </table> <hr> ⚠️ **Review coverage:** The following files were not included in this review because of the token budget: - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/data/local/InfraredDao.kt` - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/remote/RemoteModels.kt` - `android/feature/infrared/build.gradle.kts` - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/runtime/DataStoreInfraredRuntimeModeStore.kt` - `android/feature/infrared/src/androidTest/kotlin/com/flagship/abox/manager/infrared/remote/InfraredRemoteScreenTest.kt` - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/data/local/InfraredMappers.kt` - `absdk-bridge/src/test/kotlin/com/flagship/abox/manager/absdk/bridge/AbsdkSerializationTest.kt` - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/ui/InfraredControlUiState.kt` - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/data/local/InfraredEntities.kt` - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/runtime/InfraredRuntimeProfileResolver.kt` - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/runtime/InfraredRuntimeMode.kt` - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/data/local/InfraredDatabase.kt` - `android/app/build.gradle.kts` - `android/feature/infrared/schemas/com.flagship.abox.manager.infrared.data.local.InfraredDatabase/1.json` - `android/app/src/main/res/values-zh-rCN/strings.xml` - `android/app/src/main/res/values/strings.xml` - `android/feature/infrared/src/main/AndroidManifest.xml`
KremeCN added 1 commit 2026-08-25 13:25:14 +08:00
pr-agent-bot bot reviewed 2026-08-25 13:28:51 +08:00
pr-agent-bot bot left a comment

Inline comment

Inline comment
@@ -169,5 +169,5 @@
}
}
fun onButtonClick(spec: RemoteButtonSpec) {
val currentProfile = profileId ?: return

设备竞态

savedDeviceName 尚未从持久化配置加载时会直接回退到 DEFAULT_DEVICE_NAME。如果用户快速开启学习模式并点击按键,而实际配置的是其他设备,学习请求会发送给 ir-1,并产生不会显示在当前设备面板中的错误键记录。应在配置成功加载前禁用操作或直接返回,而不是把“尚未加载”解释为默认设备。

**设备竞态** `savedDeviceName` 尚未从持久化配置加载时会直接回退到 `DEFAULT_DEVICE_NAME`。如果用户快速开启学习模式并点击按键,而实际配置的是其他设备,学习请求会发送给 `ir-1`,并产生不会显示在当前设备面板中的错误键记录。应在配置成功加载前禁用操作或直接返回,而不是把“尚未加载”解释为默认设备。 <!-- pr-agent-dedup: 20ea2fda4715 --> <!-- pr-agent-key-issue-location: 702140543860 -->
pr-agent-bot bot reviewed 2026-08-25 13:28:51 +08:00
pr-agent-bot bot left a comment

Inline comment

Inline comment
@@ -172,5 +172,5 @@
fun onButtonClick(spec: RemoteButtonSpec) {
val currentProfile = profileId ?: return
val currentDevice = state.value.savedDeviceName ?: DEFAULT_DEVICE_NAME
if (isLearningModeFlow.value) {

并发冲突

发送信号期间仍可开启学习模式,而学习分支没有检查 sendingKeySlotFlow。此时再次点击按键会让发送和学习请求同时操作同一红外网关及设备,可能导致学习失败或捕获到错误信号。应在两种操作之间实施统一互斥,或在发送完成前禁用学习模式开关。

**并发冲突** 发送信号期间仍可开启学习模式,而学习分支没有检查 `sendingKeySlotFlow`。此时再次点击按键会让发送和学习请求同时操作同一红外网关及设备,可能导致学习失败或捕获到错误信号。应在两种操作之间实施统一互斥,或在发送完成前禁用学习模式开关。 <!-- pr-agent-dedup: 69d5674e86b4 --> <!-- pr-agent-key-issue-location: f2db1b110ebc -->
pr-agent-bot bot reviewed 2026-08-25 13:28:51 +08:00
pr-agent-bot bot left a comment

Inline comment

Inline comment
@@ -25,5 +25,5 @@
)
object RemoteSceneConfig {
object AirConditioner {
val POWER = RemoteButtonSpec("AC_POWER", "1", R.string.infrared_key_ac_power, "空调-电源", "⏻")

本地化缺失

所有按键的 displayName 都硬编码为中文,并被用于持久化名称和 Snackbar 消息;因此非中文语言环境下仍会显示并保存中文文本,尽管每个按键已经提供了 nameRes。应通过资源解析显示名称,避免将本地化文案固定在模型中。

**本地化缺失** 所有按键的 `displayName` 都硬编码为中文,并被用于持久化名称和 Snackbar 消息;因此非中文语言环境下仍会显示并保存中文文本,尽管每个按键已经提供了 `nameRes`。应通过资源解析显示名称,避免将本地化文案固定在模型中。 <!-- pr-agent-dedup: 40f1e05bb9d8 --> <!-- pr-agent-key-issue-location: c48521f26417 -->
pr-agent-bot bot reviewed 2026-08-25 13:28:51 +08:00
pr-agent-bot bot left a comment

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 5 🔵🔵🔵🔵🔵
🧪 PR contains tests
🔒 No security concerns identified

⚠️ Review coverage: The following files were not included in this review because of the token budget:

  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/data/local/InfraredDao.kt
  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/application/InfraredLearningCoordinator.kt
  • android/feature/infrared/build.gradle.kts
  • android/feature/infrared/src/androidTest/kotlin/com/flagship/abox/manager/infrared/remote/InfraredRemoteScreenTest.kt
  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/data/local/InfraredMappers.kt
  • absdk-bridge/src/test/kotlin/com/flagship/abox/manager/absdk/bridge/AbsdkSerializationTest.kt
  • android/feature/infrared/src/test/kotlin/com/flagship/abox/manager/infrared/remote/RemoteModelsTest.kt
  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/data/local/InfraredDatabase.kt
  • build.gradle.kts
  • android/app/build.gradle.kts
  • android/feature/infrared/src/main/res/values-zh-rCN/strings.xml
  • android/feature/infrared/src/main/res/values/strings.xml
  • android/feature/infrared/schemas/com.flagship.abox.manager.infrared.data.local.InfraredDatabase/1.json
  • android/app/src/main/res/values-zh-rCN/strings.xml
  • android/app/src/main/res/values/strings.xml
  • android/feature/infrared/src/main/AndroidManifest.xml
## PR Reviewer Guide 🔍 Here are some key observations to aid the review process: <table> <tr><td>⏱️&nbsp;<strong>Estimated effort to review</strong>: 5 🔵🔵🔵🔵🔵</td></tr> <tr><td>🧪&nbsp;<strong>PR contains tests</strong></td></tr> <tr><td>🔒&nbsp;<strong>No security concerns identified</strong></td></tr> </table> <hr> ⚠️ **Review coverage:** The following files were not included in this review because of the token budget: - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/data/local/InfraredDao.kt` - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/application/InfraredLearningCoordinator.kt` - `android/feature/infrared/build.gradle.kts` - `android/feature/infrared/src/androidTest/kotlin/com/flagship/abox/manager/infrared/remote/InfraredRemoteScreenTest.kt` - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/data/local/InfraredMappers.kt` - `absdk-bridge/src/test/kotlin/com/flagship/abox/manager/absdk/bridge/AbsdkSerializationTest.kt` - `android/feature/infrared/src/test/kotlin/com/flagship/abox/manager/infrared/remote/RemoteModelsTest.kt` - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/data/local/InfraredDatabase.kt` - `build.gradle.kts` - `android/app/build.gradle.kts` - `android/feature/infrared/src/main/res/values-zh-rCN/strings.xml` - `android/feature/infrared/src/main/res/values/strings.xml` - `android/feature/infrared/schemas/com.flagship.abox.manager.infrared.data.local.InfraredDatabase/1.json` - `android/app/src/main/res/values-zh-rCN/strings.xml` - `android/app/src/main/res/values/strings.xml` - `android/feature/infrared/src/main/AndroidManifest.xml`
KremeCN added 1 commit 2026-08-25 13:39:14 +08:00
pr-agent-bot bot reviewed 2026-08-25 13:42:32 +08:00
pr-agent-bot bot left a comment

Inline comment

Inline comment
@@ -196,5 +196,5 @@
slotInt.toString()
} else {
nextAvailableKeySlot().toString()
}
learningCoordinator.learn(currentProfile, savedDevice, trimmedName, slotString)

结果丢失

learningCoordinator.learn() 和 retry() 返回的异步结果被直接丢弃。遇到重复槽位、设备离线或认证失效时,不会调用 handleDomainError 或触发会话失效处理;如果新记录尚未插入,列表也不会变化,用户只会看到对话框关闭。应在 viewModelScope 中等待结果并统一处理失败。

**结果丢失** `learningCoordinator.learn()` 和 `retry()` 返回的异步结果被直接丢弃。遇到重复槽位、设备离线或认证失效时,不会调用 `handleDomainError` 或触发会话失效处理;如果新记录尚未插入,列表也不会变化,用户只会看到对话框关闭。应在 `viewModelScope` 中等待结果并统一处理失败。 <!-- pr-agent-dedup: a29431c5b567 --> <!-- pr-agent-key-issue-location: 76371e98dff6 -->
pr-agent-bot bot reviewed 2026-08-25 13:42:32 +08:00
pr-agent-bot bot left a comment

Inline comment

Inline comment
@@ -186,5 +186,5 @@
}
}
fun onButtonClick(spec: RemoteButtonSpec) {
val currentProfile = profileId ?: return

设备错绑

配置尚未加载时,savedDeviceName 为 null,这里会直接回退到 ir-1,但遥控按键仍可操作。若该配置实际保存了其他设备名,冷启动后快速开启学习并点击按键会将按键绑定到错误设备,配置加载完成后该按键还会从当前列表消失。应在设备配置解析完成前禁用学习操作。

**设备错绑** 配置尚未加载时,`savedDeviceName` 为 `null`,这里会直接回退到 `ir-1`,但遥控按键仍可操作。若该配置实际保存了其他设备名,冷启动后快速开启学习并点击按键会将按键绑定到错误设备,配置加载完成后该按键还会从当前列表消失。应在设备配置解析完成前禁用学习操作。 <!-- pr-agent-dedup: 64f51ac400f4 --> <!-- pr-agent-key-issue-location: 40951f9578b1 -->
pr-agent-bot bot reviewed 2026-08-25 13:42:32 +08:00
pr-agent-bot bot left a comment

Inline comment

Inline comment
@@ -169,5 +169,5 @@
}
}
}
fun nextAvailableKeySlot(): Int {

容量回绕

当 1 到 100 的槽位全部被占用时,该方法仍返回已占用的槽位 1,导致新增对话框提供一个必然触发唯一约束失败的默认值。应返回无可用槽位的状态,并禁用新增操作或提示容量已满。

**容量回绕** 当 1 到 100 的槽位全部被占用时,该方法仍返回已占用的槽位 1,导致新增对话框提供一个必然触发唯一约束失败的默认值。应返回无可用槽位的状态,并禁用新增操作或提示容量已满。 <!-- pr-agent-dedup: 8d26af909fc3 --> <!-- pr-agent-key-issue-location: c6973e0768bd -->
pr-agent-bot bot reviewed 2026-08-25 13:42:33 +08:00
pr-agent-bot bot left a comment

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 5 🔵🔵🔵🔵🔵
🧪 PR contains tests
🔒 No security concerns identified

⚠️ Review coverage: The following files were not included in this review because of the token budget:

  • android/app/src/main/kotlin/com/flagship/abox/manager/ui/ABoxManagerApp.kt
  • absdk-bridge/src/test/kotlin/com/flagship/abox/manager/absdk/bridge/AbsdkDeviceBridgeTest.kt
  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/remote/RemoteModels.kt
  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/data/local/InfraredDao.kt
  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/application/InfraredLearningCoordinator.kt
  • android/feature/infrared/build.gradle.kts
  • android/feature/infrared/src/androidTest/kotlin/com/flagship/abox/manager/infrared/remote/InfraredRemoteScreenTest.kt
  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/data/local/InfraredMappers.kt
  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/ui/InfraredControlUiState.kt
  • absdk-bridge/src/test/kotlin/com/flagship/abox/manager/absdk/bridge/AbsdkSerializationTest.kt
  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/data/local/InfraredEntities.kt
  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/data/local/InfraredDatabase.kt
  • android/app/build.gradle.kts
  • android/feature/infrared/src/main/res/values-zh-rCN/strings.xml
  • android/feature/infrared/src/main/res/values/strings.xml
  • android/feature/infrared/schemas/com.flagship.abox.manager.infrared.data.local.InfraredDatabase/1.json
  • android/app/src/main/res/values-zh-rCN/strings.xml
  • android/app/src/main/res/values/strings.xml
## PR Reviewer Guide 🔍 Here are some key observations to aid the review process: <table> <tr><td>⏱️&nbsp;<strong>Estimated effort to review</strong>: 5 🔵🔵🔵🔵🔵</td></tr> <tr><td>🧪&nbsp;<strong>PR contains tests</strong></td></tr> <tr><td>🔒&nbsp;<strong>No security concerns identified</strong></td></tr> </table> <hr> ⚠️ **Review coverage:** The following files were not included in this review because of the token budget: - `android/app/src/main/kotlin/com/flagship/abox/manager/ui/ABoxManagerApp.kt` - `absdk-bridge/src/test/kotlin/com/flagship/abox/manager/absdk/bridge/AbsdkDeviceBridgeTest.kt` - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/remote/RemoteModels.kt` - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/data/local/InfraredDao.kt` - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/application/InfraredLearningCoordinator.kt` - `android/feature/infrared/build.gradle.kts` - `android/feature/infrared/src/androidTest/kotlin/com/flagship/abox/manager/infrared/remote/InfraredRemoteScreenTest.kt` - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/data/local/InfraredMappers.kt` - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/ui/InfraredControlUiState.kt` - `absdk-bridge/src/test/kotlin/com/flagship/abox/manager/absdk/bridge/AbsdkSerializationTest.kt` - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/data/local/InfraredEntities.kt` - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/data/local/InfraredDatabase.kt` - `android/app/build.gradle.kts` - `android/feature/infrared/src/main/res/values-zh-rCN/strings.xml` - `android/feature/infrared/src/main/res/values/strings.xml` - `android/feature/infrared/schemas/com.flagship.abox.manager.infrared.data.local.InfraredDatabase/1.json` - `android/app/src/main/res/values-zh-rCN/strings.xml` - `android/app/src/main/res/values/strings.xml`
KremeCN added 6 commits 2026-08-25 14:51:00 +08:00
KremeCN merged commit 09c352457d into main 2026-08-25 14:51:34 +08:00
KremeCN deleted branch feat/moler-infrared-control 2026-08-25 14:51:34 +08:00
pr-agent-bot bot reviewed 2026-08-25 14:54:14 +08:00
pr-agent-bot bot left a comment

Inline comment

Inline comment
@@ -44,5 +44,5 @@
@Singleton
fun provideInfraredDatabase(
@ApplicationContext context: Context,
): InfraredDatabase {
return Room.databaseBuilder(

数据丢失

使用 fallbackToDestructiveMigration() 会在数据库版本升级且缺少对应迁移时直接清空整个红外数据库,导致用户已录制的红外码和遥控器配置不可恢复地丢失。应为生产数据库提供显式 Migration,避免启用破坏性回退。

**数据丢失** 使用 `fallbackToDestructiveMigration()` 会在数据库版本升级且缺少对应迁移时直接清空整个红外数据库,导致用户已录制的红外码和遥控器配置不可恢复地丢失。应为生产数据库提供显式 `Migration`,避免启用破坏性回退。 <!-- pr-agent-dedup: 80d09de2eaf5 --> <!-- pr-agent-key-issue-location: fb291c03ba0c -->
pr-agent-bot bot reviewed 2026-08-25 14:54:14 +08:00
pr-agent-bot bot left a comment

Inline comment

Inline comment
@@ -82,5 +82,5 @@
override suspend fun createRemote(
remote: VirtualRemote,
initialBindings: List<RemoteKeyBinding>,
): DomainResult<VirtualRemoteId> = try {
dao.insertVirtualRemote(remote.toEntity())

事务缺失

创建遥控器由两次独立 DAO 写入组成;如果按键绑定写入失败,方法会返回失败,但遥控器记录已经提交,列表中将留下无按键的半成品,重试还可能继续产生不一致数据。应将遥控器和初始绑定的写入封装在同一个 Room 事务中。

**事务缺失** 创建遥控器由两次独立 DAO 写入组成;如果按键绑定写入失败,方法会返回失败,但遥控器记录已经提交,列表中将留下无按键的半成品,重试还可能继续产生不一致数据。应将遥控器和初始绑定的写入封装在同一个 Room 事务中。 <!-- pr-agent-dedup: b7de730ee045 --> <!-- pr-agent-key-issue-location: 3d41b845c210 -->
pr-agent-bot bot reviewed 2026-08-25 14:54:14 +08:00
pr-agent-bot bot left a comment

Inline comment

Inline comment
@@ -573,5 +573,5 @@
}
},
confirmButton = {
Button(
onClick = {

错误关联

自定义设备没有品牌选择,但确认时仍始终传入 selectedBrand.id,因此新建的自定义遥控器会错误关联此前选中的电视或其他预设型号。应在 CUSTOM 类型下传入 null,并对没有匹配预设的设备类型单独处理。

**错误关联** 自定义设备没有品牌选择,但确认时仍始终传入 `selectedBrand.id`,因此新建的自定义遥控器会错误关联此前选中的电视或其他预设型号。应在 `CUSTOM` 类型下传入 `null`,并对没有匹配预设的设备类型单独处理。 <!-- pr-agent-dedup: df40ad90657e --> <!-- pr-agent-key-issue-location: dfc88f482c2f -->
pr-agent-bot bot reviewed 2026-08-25 14:54:14 +08:00
pr-agent-bot bot left a comment

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 5 🔵🔵🔵🔵🔵
🧪 PR contains tests
🔒 No security concerns identified

⚠️ Review coverage: The following files were not included in this review because of the token budget:

  • android/feature/infrared/src/androidTest/kotlin/com/flagship/abox/manager/infrared/data/local/RoomInfraredKeyRepositoryTest.kt
  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/hub/InfraredHubViewModel.kt
  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/data/local/RoomInfraredKeyRepository.kt
  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/data/local/InfraredDao.kt
  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/remote/ui/AcRemotePanel.kt
  • android/app/src/main/kotlin/com/flagship/abox/manager/ui/ABoxManagerApp.kt
  • absdk-bridge/src/test/kotlin/com/flagship/abox/manager/absdk/bridge/AbsdkDeviceBridgeTest.kt
  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/remote/RemoteModels.kt
  • android/feature/infrared/src/androidTest/kotlin/com/flagship/abox/manager/infrared/ui/InfraredControlScreenTest.kt
  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/application/InfraredLearningCoordinator.kt
  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/data/local/InfraredEntities.kt
  • android/feature/infrared/build.gradle.kts
  • android/feature/infrared/src/androidTest/kotlin/com/flagship/abox/manager/infrared/remote/InfraredRemoteScreenTest.kt
  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/data/local/InfraredMappers.kt
  • absdk-bridge/src/test/kotlin/com/flagship/abox/manager/absdk/bridge/AbsdkSerializationTest.kt
  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/hub/InfraredHubUiState.kt
  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/panel/RemotePanelUiState.kt
  • android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/data/local/InfraredDatabase.kt
  • android/feature/infrared/schemas/com.flagship.abox.manager.infrared.data.local.InfraredDatabase/1.json
  • android/app/src/main/res/values-zh-rCN/strings.xml
  • android/app/src/main/res/values/strings.xml
  • android/feature/infrared/src/main/AndroidManifest.xml
## PR Reviewer Guide 🔍 Here are some key observations to aid the review process: <table> <tr><td>⏱️&nbsp;<strong>Estimated effort to review</strong>: 5 🔵🔵🔵🔵🔵</td></tr> <tr><td>🧪&nbsp;<strong>PR contains tests</strong></td></tr> <tr><td>🔒&nbsp;<strong>No security concerns identified</strong></td></tr> </table> <hr> ⚠️ **Review coverage:** The following files were not included in this review because of the token budget: - `android/feature/infrared/src/androidTest/kotlin/com/flagship/abox/manager/infrared/data/local/RoomInfraredKeyRepositoryTest.kt` - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/hub/InfraredHubViewModel.kt` - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/data/local/RoomInfraredKeyRepository.kt` - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/data/local/InfraredDao.kt` - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/remote/ui/AcRemotePanel.kt` - `android/app/src/main/kotlin/com/flagship/abox/manager/ui/ABoxManagerApp.kt` - `absdk-bridge/src/test/kotlin/com/flagship/abox/manager/absdk/bridge/AbsdkDeviceBridgeTest.kt` - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/remote/RemoteModels.kt` - `android/feature/infrared/src/androidTest/kotlin/com/flagship/abox/manager/infrared/ui/InfraredControlScreenTest.kt` - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/application/InfraredLearningCoordinator.kt` - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/data/local/InfraredEntities.kt` - `android/feature/infrared/build.gradle.kts` - `android/feature/infrared/src/androidTest/kotlin/com/flagship/abox/manager/infrared/remote/InfraredRemoteScreenTest.kt` - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/data/local/InfraredMappers.kt` - `absdk-bridge/src/test/kotlin/com/flagship/abox/manager/absdk/bridge/AbsdkSerializationTest.kt` - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/hub/InfraredHubUiState.kt` - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/panel/RemotePanelUiState.kt` - `android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/data/local/InfraredDatabase.kt` - `android/feature/infrared/schemas/com.flagship.abox.manager.infrared.data.local.InfraredDatabase/1.json` - `android/app/src/main/res/values-zh-rCN/strings.xml` - `android/app/src/main/res/values/strings.xml` - `android/feature/infrared/src/main/AndroidManifest.xml`
KremeCN changed title from Feat/moler infrared control to feat(infrared): add infrared control, learning, and virtual remote module 2026-08-25 15:08:17 +08:00
Sign in to join this conversation.
No Reviewers
No labels
3 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Flagship/abox-mgr#9