From 594bfdd3059c06ef2878ba787094c85bdb9add57 Mon Sep 17 00:00:00 2001 From: alZerNest <2223976172@qq.com> Date: Sun, 23 Aug 2026 16:30:37 +0800 Subject: [PATCH 1/4] Add device control list UI Changes: - Add Kotlin Compose device list and device control placeholder screens. - Register the new device-control navigation graph from the existing app shell. - Preserve the current foundation and placeholder flow. Documentation: - Added English and Chinese change log under docs/device-control-compose-ui-change-log.md for team review. --- .../devicecontrol/DeviceControlComponents.kt | 92 +++++ .../ui/devicecontrol/DeviceControlRoutes.kt | 29 ++ .../ui/devicecontrol/DeviceListScreen.kt | 80 ++++ .../ui/devicecontrol/InfraredControlScreen.kt | 75 ++++ .../ui/devicecontrol/SocketControlScreen.kt | 60 +++ .../TemperatureHumidityScreen.kt | 57 +++ docs/device-control-compose-ui-change-log.md | 344 ++++++++++++++++++ 7 files changed, 737 insertions(+) create mode 100644 android/app/src/main/kotlin/com/flagship/abox/manager/ui/devicecontrol/DeviceControlComponents.kt create mode 100644 android/app/src/main/kotlin/com/flagship/abox/manager/ui/devicecontrol/DeviceControlRoutes.kt create mode 100644 android/app/src/main/kotlin/com/flagship/abox/manager/ui/devicecontrol/DeviceListScreen.kt create mode 100644 android/app/src/main/kotlin/com/flagship/abox/manager/ui/devicecontrol/InfraredControlScreen.kt create mode 100644 android/app/src/main/kotlin/com/flagship/abox/manager/ui/devicecontrol/SocketControlScreen.kt create mode 100644 android/app/src/main/kotlin/com/flagship/abox/manager/ui/devicecontrol/TemperatureHumidityScreen.kt create mode 100644 docs/device-control-compose-ui-change-log.md diff --git a/android/app/src/main/kotlin/com/flagship/abox/manager/ui/devicecontrol/DeviceControlComponents.kt b/android/app/src/main/kotlin/com/flagship/abox/manager/ui/devicecontrol/DeviceControlComponents.kt new file mode 100644 index 0000000..593cd2b --- /dev/null +++ b/android/app/src/main/kotlin/com/flagship/abox/manager/ui/devicecontrol/DeviceControlComponents.kt @@ -0,0 +1,92 @@ +package com.flagship.abox.manager.ui.devicecontrol + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ColumnScope +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.OutlinedButton +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import com.flagship.abox.manager.designsystem.ABoxButton + +@Composable +internal fun DevicePageScaffold( + title: String, + description: String, + onBackClick: () -> Unit, + modifier: Modifier = Modifier, + content: @Composable ColumnScope.() -> Unit, +) { + Column( + modifier = modifier.padding(32.dp), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center, + ) { + Text( + text = title, + style = MaterialTheme.typography.headlineLarge, + textAlign = TextAlign.Center, + ) + Text( + text = description, + modifier = Modifier.padding(top = 12.dp, bottom = 24.dp), + style = MaterialTheme.typography.bodyLarge, + textAlign = TextAlign.Center, + ) + content() + OutlinedButton( + onClick = onBackClick, + modifier = Modifier + .fillMaxWidth() + .padding(top = 24.dp), + ) { + Text("返回设备列表") + } + } +} + +@Composable +internal fun PrimaryActionButton( + text: String, + onClick: () -> Unit, + modifier: Modifier = Modifier, +) { + ABoxButton( + onClick = onClick, + modifier = modifier.fillMaxWidth(), + ) { + Text(text) + } +} + +@Composable +internal fun TwoColumnActions( + leftText: String, + rightText: String, + onLeftClick: () -> Unit, + onRightClick: () -> Unit, + modifier: Modifier = Modifier, +) { + Row( + modifier = modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.spacedBy(16.dp), + ) { + PrimaryActionButton( + text = leftText, + onClick = onLeftClick, + modifier = Modifier.weight(1f), + ) + PrimaryActionButton( + text = rightText, + onClick = onRightClick, + modifier = Modifier.weight(1f), + ) + } +} diff --git a/android/app/src/main/kotlin/com/flagship/abox/manager/ui/devicecontrol/DeviceControlRoutes.kt b/android/app/src/main/kotlin/com/flagship/abox/manager/ui/devicecontrol/DeviceControlRoutes.kt new file mode 100644 index 0000000..71b5a56 --- /dev/null +++ b/android/app/src/main/kotlin/com/flagship/abox/manager/ui/devicecontrol/DeviceControlRoutes.kt @@ -0,0 +1,29 @@ +package com.flagship.abox.manager.ui.devicecontrol + +import androidx.navigation.NavGraphBuilder +import androidx.navigation.NavHostController +import androidx.navigation.compose.composable + +const val DEVICE_LIST_ROUTE = "device_list" +private const val SOCKET_ROUTE = "device_socket" +private const val TH_ROUTE = "device_temperature_humidity" +private const val INFRARED_ROUTE = "device_infrared" + +fun NavGraphBuilder.deviceControlGraph(navController: NavHostController) { + composable(DEVICE_LIST_ROUTE) { + DeviceListScreen( + onSocketClick = { navController.navigate(SOCKET_ROUTE) }, + onTemperatureHumidityClick = { navController.navigate(TH_ROUTE) }, + onInfraredClick = { navController.navigate(INFRARED_ROUTE) }, + ) + } + composable(SOCKET_ROUTE) { + SocketControlScreen(onBackClick = navController::popBackStack) + } + composable(TH_ROUTE) { + TemperatureHumidityScreen(onBackClick = navController::popBackStack) + } + composable(INFRARED_ROUTE) { + InfraredControlScreen(onBackClick = navController::popBackStack) + } +} diff --git a/android/app/src/main/kotlin/com/flagship/abox/manager/ui/devicecontrol/DeviceListScreen.kt b/android/app/src/main/kotlin/com/flagship/abox/manager/ui/devicecontrol/DeviceListScreen.kt new file mode 100644 index 0000000..a710c94 --- /dev/null +++ b/android/app/src/main/kotlin/com/flagship/abox/manager/ui/devicecontrol/DeviceListScreen.kt @@ -0,0 +1,80 @@ +package com.flagship.abox.manager.ui.devicecontrol + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import com.flagship.abox.manager.designsystem.ABoxButton + +@Composable +fun DeviceListScreen( + onSocketClick: () -> Unit, + onTemperatureHumidityClick: () -> Unit, + onInfraredClick: () -> Unit, + modifier: Modifier = Modifier, +) { + Column( + modifier = modifier + .fillMaxSize() + .padding(32.dp), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center, + ) { + Text( + text = "设备列表", + style = MaterialTheme.typography.headlineLarge, + textAlign = TextAlign.Center, + ) + Text( + text = "选择需要控制或查看状态的 ABox 终端设备。", + modifier = Modifier.padding(top = 12.dp, bottom = 32.dp), + style = MaterialTheme.typography.bodyLarge, + textAlign = TextAlign.Center, + ) + DeviceEntryButton( + text = "智能插座", + description = "查看插座状态,执行打开或关闭操作。", + onClick = onSocketClick, + ) + DeviceEntryButton( + text = "温湿度传感器", + description = "查询当前温度与湿度读数。", + onClick = onTemperatureHumidityClick, + ) + DeviceEntryButton( + text = "红外遥控器", + description = "发送红外按键,或进入红外学习流程。", + onClick = onInfraredClick, + ) + } +} + +@Composable +private fun DeviceEntryButton( + text: String, + description: String, + onClick: () -> Unit, +) { + ABoxButton( + onClick = onClick, + modifier = Modifier + .fillMaxWidth() + .padding(top = 16.dp), + ) { + Column( + modifier = Modifier.padding(vertical = 8.dp), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Text(text = text, style = MaterialTheme.typography.titleMedium) + Text(text = description, style = MaterialTheme.typography.bodySmall) + } + } +} diff --git a/android/app/src/main/kotlin/com/flagship/abox/manager/ui/devicecontrol/InfraredControlScreen.kt b/android/app/src/main/kotlin/com/flagship/abox/manager/ui/devicecontrol/InfraredControlScreen.kt new file mode 100644 index 0000000..8d06d7f --- /dev/null +++ b/android/app/src/main/kotlin/com/flagship/abox/manager/ui/devicecontrol/InfraredControlScreen.kt @@ -0,0 +1,75 @@ +package com.flagship.abox.manager.ui.devicecontrol + +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.OutlinedTextField +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp + +@Composable +fun InfraredControlScreen( + onBackClick: () -> Unit, + modifier: Modifier = Modifier, +) { + var deviceName by remember { mutableStateOf("红外遥控器") } + var key by remember { mutableStateOf("") } + var statusText by remember { mutableStateOf("输入设备名称和按键 KEY 后执行红外操作") } + + DevicePageScaffold( + title = "红外遥控器", + description = "发送红外按键或学习新按键。后续可接入 InfraredGateway。", + onBackClick = onBackClick, + modifier = modifier.fillMaxSize(), + ) { + OutlinedTextField( + value = deviceName, + onValueChange = { deviceName = it }, + modifier = Modifier.fillMaxWidth(), + label = { Text("红外设备名称") }, + singleLine = true, + ) + OutlinedTextField( + value = key, + onValueChange = { key = it }, + modifier = Modifier + .fillMaxWidth() + .padding(top = 16.dp), + label = { Text("按键 KEY,例如 POWER") }, + singleLine = true, + ) + TwoColumnActions( + leftText = "发送", + rightText = "学习", + onLeftClick = { + statusText = if (key.isBlank()) { + "请先输入按键 KEY。" + } else { + "已请求发送 $key(待接入真实红外发送)" + } + }, + onRightClick = { + statusText = if (key.isBlank()) { + "请先输入按键 KEY。" + } else { + "已请求学习 $key(待接入真实红外学习)" + } + }, + modifier = Modifier.padding(top = 24.dp), + ) + Text( + text = statusText, + modifier = Modifier.padding(top = 20.dp), + style = MaterialTheme.typography.bodyMedium, + textAlign = TextAlign.Center, + ) + } +} diff --git a/android/app/src/main/kotlin/com/flagship/abox/manager/ui/devicecontrol/SocketControlScreen.kt b/android/app/src/main/kotlin/com/flagship/abox/manager/ui/devicecontrol/SocketControlScreen.kt new file mode 100644 index 0000000..b5e966f --- /dev/null +++ b/android/app/src/main/kotlin/com/flagship/abox/manager/ui/devicecontrol/SocketControlScreen.kt @@ -0,0 +1,60 @@ +package com.flagship.abox.manager.ui.devicecontrol + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp + +@Composable +fun SocketControlScreen( + onBackClick: () -> Unit, + modifier: Modifier = Modifier, +) { + var statusText by remember { mutableStateOf("当前状态:未知") } + + DevicePageScaffold( + title = "智能插座", + description = "Kotlin Compose 版插座控制页面。后续可接入 SocketGateway。", + onBackClick = onBackClick, + modifier = modifier.fillMaxSize(), + ) { + Text( + text = statusText, + modifier = Modifier.padding(bottom = 24.dp), + style = MaterialTheme.typography.headlineSmall, + textAlign = TextAlign.Center, + ) + PrimaryActionButton( + text = "刷新状态", + onClick = { + statusText = "当前状态:等待接入真实状态查询" + }, + ) + TwoColumnActions( + leftText = "打开", + rightText = "关闭", + onLeftClick = { + statusText = "当前状态:已请求打开(待接入真实控制)" + }, + onRightClick = { + statusText = "当前状态:已请求关闭(待接入真实控制)" + }, + modifier = Modifier.padding(top = 16.dp), + ) + Text( + text = "建议后续调用 SocketGateway.getStatus / setPower,而不是直接调用 ABSDK。", + modifier = Modifier.padding(top = 20.dp), + style = MaterialTheme.typography.bodyMedium, + textAlign = TextAlign.Center, + ) + } +} diff --git a/android/app/src/main/kotlin/com/flagship/abox/manager/ui/devicecontrol/TemperatureHumidityScreen.kt b/android/app/src/main/kotlin/com/flagship/abox/manager/ui/devicecontrol/TemperatureHumidityScreen.kt new file mode 100644 index 0000000..18508db --- /dev/null +++ b/android/app/src/main/kotlin/com/flagship/abox/manager/ui/devicecontrol/TemperatureHumidityScreen.kt @@ -0,0 +1,57 @@ +package com.flagship.abox.manager.ui.devicecontrol + +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp + +@Composable +fun TemperatureHumidityScreen( + onBackClick: () -> Unit, + modifier: Modifier = Modifier, +) { + var temperature by remember { mutableStateOf("-- ℃") } + var humidity by remember { mutableStateOf("-- %") } + var statusText by remember { mutableStateOf("点击按钮查询当前温湿度") } + + DevicePageScaffold( + title = "温湿度状态", + description = "显示温度与湿度读数。后续可接入 TemperatureHumidityGateway。", + onBackClick = onBackClick, + modifier = modifier.fillMaxSize(), + ) { + Text( + text = "温度:$temperature", + style = MaterialTheme.typography.headlineSmall, + textAlign = TextAlign.Center, + ) + Text( + text = "湿度:$humidity", + modifier = Modifier.padding(top = 12.dp, bottom = 24.dp), + style = MaterialTheme.typography.headlineSmall, + textAlign = TextAlign.Center, + ) + PrimaryActionButton( + text = "刷新温湿度", + onClick = { + temperature = "待接入" + humidity = "待接入" + statusText = "已触发刷新动作,等待接入真实 ABSDK bridge。" + }, + ) + Text( + text = statusText, + modifier = Modifier.padding(top = 20.dp), + style = MaterialTheme.typography.bodyMedium, + textAlign = TextAlign.Center, + ) + } +} diff --git a/docs/device-control-compose-ui-change-log.md b/docs/device-control-compose-ui-change-log.md new file mode 100644 index 0000000..bfefaec --- /dev/null +++ b/docs/device-control-compose-ui-change-log.md @@ -0,0 +1,344 @@ +# Device Control Compose UI Change Log + +Branch: `feature/device-control-compose-ui` + +## Summary + +This branch adds a Kotlin Jetpack Compose implementation for the device-control UI owned by this feature area. + +The implementation introduces a new device-control entry point and a group of Compose screens for the three device types required by the current project scope: + +- Socket device control +- Temperature/humidity status display +- Infrared remote control actions + +The existing placeholder/foundation app flow is preserved. The new device-control flow is added as an additional navigation path instead of replacing the existing screens. + +## Modified Files + +### `android/app/src/main/kotlin/com/flagship/abox/manager/ui/ABoxManagerApp.kt` + +Changes: + +- Added an entry button on the foundation screen for the Kotlin device-control UI. +- Registered the new device-control navigation graph in the existing `NavHost`. +- Preserved the existing foundation and placeholder routes. + +Purpose: + +- Allows teammates to open the newly added device-control screens from the current Compose app shell. + +## Added Files + +### `android/app/src/main/kotlin/com/flagship/abox/manager/ui/devicecontrol/DeviceControlRoutes.kt` + +Adds navigation routes and a small navigation graph for the device-control feature. + +Routes included: + +- Device list +- Socket control +- Temperature/humidity status +- Infrared control + +### `android/app/src/main/kotlin/com/flagship/abox/manager/ui/devicecontrol/DeviceListScreen.kt` + +Adds the top-level device list screen. + +The screen contains entries for: + +- Smart socket +- Temperature/humidity sensor +- Infrared remote controller + +Each entry navigates to its corresponding Compose screen. + +### `android/app/src/main/kotlin/com/flagship/abox/manager/ui/devicecontrol/DeviceControlComponents.kt` + +Adds shared UI components used by the device-control screens. + +Included components: + +- Shared device page scaffold +- Primary action button wrapper +- Two-column action button row + +Purpose: + +- Keeps repeated screen layout and action button patterns consistent across device screens. + +### `android/app/src/main/kotlin/com/flagship/abox/manager/ui/devicecontrol/SocketControlScreen.kt` + +Adds a Compose screen for socket control. + +Current scope: + +- Displays socket status text. +- Provides actions for refreshing status, turning on, and turning off. +- Uses local Compose state as a placeholder. + +Future integration point: + +- Replace placeholder state handling with `SocketGateway.getStatus(...)` and `SocketGateway.setPower(...)` through a ViewModel. + +### `android/app/src/main/kotlin/com/flagship/abox/manager/ui/devicecontrol/TemperatureHumidityScreen.kt` + +Adds a Compose screen for temperature/humidity display. + +Current scope: + +- Displays temperature and humidity values. +- Provides a refresh action. +- Uses local Compose state as a placeholder. + +Future integration point: + +- Replace placeholder state handling with `TemperatureHumidityGateway.getReading(...)` through a ViewModel. + +### `android/app/src/main/kotlin/com/flagship/abox/manager/ui/devicecontrol/InfraredControlScreen.kt` + +Adds a Compose screen for infrared remote operations. + +Current scope: + +- Allows entering an infrared device name. +- Allows entering an infrared key name. +- Provides send and learn actions. +- Uses local Compose state as a placeholder. + +Future integration point: + +- Replace placeholder state handling with `InfraredGateway.sendKey(...)` and `InfraredGateway.learnKey(...)` through a ViewModel. + +## Architecture Notes + +This branch follows the current repository direction: + +- Kotlin instead of Java +- Jetpack Compose instead of XML layouts +- Single-Activity UI instead of multiple Activities +- Navigation Compose instead of `Intent`-based Activity navigation +- Existing design-system wrappers where practical + +This branch intentionally does not copy the previous Java/XML experiment module into the team repository. + +## ABSDK Integration Notes + +The UI screens added in this branch do not call ABSDK directly. + +The repository already contains domain abstractions and an ABSDK bridge: + +- `SocketGateway` +- `TemperatureHumidityGateway` +- `InfraredGateway` +- `AbsdkBridge` +- `VendorAbsdkClient` + +Recommended next step: + +- Add ViewModels for the device-control screens. +- Inject/use the gateway interfaces from the domain layer. +- Expose screen UI state from ViewModels to the Compose screens. + +## Scope Intentionally Not Included + +This branch does not include: + +- Direct ABSDK calls from Compose UI +- New Java Activity classes +- XML layout files +- A separate Android module copied from the experiment project +- Full session/login integration for device gateway calls +- Persistent infrared key storage + +These should be added in later branches or follow-up commits after team architecture review. + +## Files Intended for Commit + +The intended commit scope is limited to: + +```text +android/app/src/main/kotlin/com/flagship/abox/manager/ui/ABoxManagerApp.kt +android/app/src/main/kotlin/com/flagship/abox/manager/ui/devicecontrol/ +docs/device-control-compose-ui-change-log.md +``` + +`settings.gradle.kts` is intentionally not part of this feature commit unless the team decides the local toolchain resolver change is required. + +--- + +# 设备控制 Compose UI 修改说明(中文) + +分支:`feature/device-control-compose-ui` + +## 概要 + +本分支新增了一套 Kotlin Jetpack Compose 版本的设备控制 UI,用于承载当前负责的设备列表与三类终端设备页面。 + +本次新增的设备范围包括: + +- 智能插座控制 +- 温湿度状态显示 +- 红外遥控器操作 + +本分支没有替换原有的 foundation / placeholder 页面,而是在现有 Compose 应用壳中新增了一个设备控制入口。因此,原有占位流程仍然保留,设备控制流程是额外新增的导航路径。 + +## 修改文件 + +### `android/app/src/main/kotlin/com/flagship/abox/manager/ui/ABoxManagerApp.kt` + +修改内容: + +- 在 foundation 页面增加了一个进入 Kotlin 版设备控制 UI 的按钮。 +- 在现有 `NavHost` 中注册了设备控制导航图。 +- 保留了原有 foundation route 和 placeholder route。 + +作用: + +- 方便团队成员从当前 Compose 应用入口进入新增加的设备控制页面。 + +## 新增文件 + +### `android/app/src/main/kotlin/com/flagship/abox/manager/ui/devicecontrol/DeviceControlRoutes.kt` + +新增设备控制相关的 Compose Navigation 路由与导航图。 + +包含的页面路由: + +- 设备列表 +- 智能插座控制 +- 温湿度状态 +- 红外遥控器控制 + +### `android/app/src/main/kotlin/com/flagship/abox/manager/ui/devicecontrol/DeviceListScreen.kt` + +新增设备列表页面。 + +页面包含三个设备入口: + +- 智能插座 +- 温湿度传感器 +- 红外遥控器 + +每个入口会跳转到对应的 Compose 设备页面。 + +### `android/app/src/main/kotlin/com/flagship/abox/manager/ui/devicecontrol/DeviceControlComponents.kt` + +新增设备控制页面共用 UI 组件。 + +包含: + +- 通用设备页面脚手架 +- 主要操作按钮封装 +- 双按钮横向操作区 + +作用: + +- 减少各设备页面中的重复布局代码。 +- 让插座、温湿度、红外页面保持相似的视觉结构。 + +### `android/app/src/main/kotlin/com/flagship/abox/manager/ui/devicecontrol/SocketControlScreen.kt` + +新增智能插座控制页面。 + +当前实现范围: + +- 显示插座状态文本。 +- 提供刷新状态、打开、关闭三个操作入口。 +- 当前使用本地 Compose state 作为占位状态。 + +后续接入点: + +- 通过 ViewModel 接入 `SocketGateway.getStatus(...)`。 +- 通过 ViewModel 接入 `SocketGateway.setPower(...)`。 + +### `android/app/src/main/kotlin/com/flagship/abox/manager/ui/devicecontrol/TemperatureHumidityScreen.kt` + +新增温湿度状态页面。 + +当前实现范围: + +- 显示温度值。 +- 显示湿度值。 +- 提供刷新温湿度按钮。 +- 当前使用本地 Compose state 作为占位状态。 + +后续接入点: + +- 通过 ViewModel 接入 `TemperatureHumidityGateway.getReading(...)`。 + +### `android/app/src/main/kotlin/com/flagship/abox/manager/ui/devicecontrol/InfraredControlScreen.kt` + +新增红外遥控器页面。 + +当前实现范围: + +- 输入红外设备名称。 +- 输入红外按键 KEY。 +- 提供发送红外按键操作。 +- 提供学习红外按键操作。 +- 当前使用本地 Compose state 作为占位状态。 + +后续接入点: + +- 通过 ViewModel 接入 `InfraredGateway.sendKey(...)`。 +- 通过 ViewModel 接入 `InfraredGateway.learnKey(...)`。 + +## 架构说明 + +本分支遵循当前小组仓库的技术方向: + +- 使用 Kotlin,而不是 Java。 +- 使用 Jetpack Compose,而不是 XML layout。 +- 使用单 Activity 架构,而不是多个 Activity 跳转。 +- 使用 Navigation Compose,而不是 `Intent` 页面跳转。 +- 尽量复用已有 design system 中的组件封装。 + +本分支没有把之前个人实验项目中的 Java/XML 模块直接复制进团队仓库。 + +## ABSDK 接入说明 + +本次新增的 Compose UI 页面没有直接调用 ABSDK。 + +当前仓库中已经存在较正式的 domain 抽象和 ABSDK bridge: + +- `SocketGateway` +- `TemperatureHumidityGateway` +- `InfraredGateway` +- `AbsdkBridge` +- `VendorAbsdkClient` + +因此后续真实设备控制逻辑建议通过 ViewModel 调用 domain gateway,而不是在 Compose UI 中直接调用 ABSDK。 + +推荐后续工作: + +- 为插座页面新增 ViewModel。 +- 为温湿度页面新增 ViewModel。 +- 为红外页面新增 ViewModel。 +- 在 ViewModel 中调用对应 gateway。 +- 将调用结果转换为 Compose UI state。 + +## 本分支未包含的内容 + +本分支暂不包含: + +- Compose UI 中直接调用 ABSDK。 +- 新增 Java Activity。 +- 新增 XML layout。 +- 从个人实验项目复制独立 Android 模块。 +- 完整登录/session 与设备 gateway 的联动。 +- 红外学习按键的持久化存储。 + +这些内容建议在团队确认架构之后,作为后续 branch 或后续 commit 继续实现。 + +## 预期提交范围 + +本次提交范围应限制在: + +```text +android/app/src/main/kotlin/com/flagship/abox/manager/ui/ABoxManagerApp.kt +android/app/src/main/kotlin/com/flagship/abox/manager/ui/devicecontrol/ +docs/device-control-compose-ui-change-log.md +``` + +`settings.gradle.kts` 当前不属于本次设备控制 UI 功能提交范围,除非团队明确认为本地 toolchain resolver 配置也需要进入 PR。 -- 2.54.0 From 99e4a073a1dcabfba194193f7c351a1d305f8081 Mon Sep 17 00:00:00 2001 From: alZerNest <2223976172@qq.com> Date: Mon, 24 Aug 2026 21:23:11 +0800 Subject: [PATCH 2/4] Limit feature to device list component --- .../devicecontrol/DeviceControlComponents.kt | 92 ----- .../ui/devicecontrol/DeviceControlRoutes.kt | 29 -- .../ui/devicecontrol/InfraredControlScreen.kt | 75 ---- .../ui/devicecontrol/SocketControlScreen.kt | 60 ---- .../TemperatureHumidityScreen.kt | 57 ---- .../DeviceListScreen.kt | 10 +- docs/device-control-compose-ui-change-log.md | 321 +++++------------- 7 files changed, 91 insertions(+), 553 deletions(-) delete mode 100644 android/app/src/main/kotlin/com/flagship/abox/manager/ui/devicecontrol/DeviceControlComponents.kt delete mode 100644 android/app/src/main/kotlin/com/flagship/abox/manager/ui/devicecontrol/DeviceControlRoutes.kt delete mode 100644 android/app/src/main/kotlin/com/flagship/abox/manager/ui/devicecontrol/InfraredControlScreen.kt delete mode 100644 android/app/src/main/kotlin/com/flagship/abox/manager/ui/devicecontrol/SocketControlScreen.kt delete mode 100644 android/app/src/main/kotlin/com/flagship/abox/manager/ui/devicecontrol/TemperatureHumidityScreen.kt rename android/app/src/main/kotlin/com/flagship/abox/manager/ui/{devicecontrol => devicecontrollist}/DeviceListScreen.kt (88%) diff --git a/android/app/src/main/kotlin/com/flagship/abox/manager/ui/devicecontrol/DeviceControlComponents.kt b/android/app/src/main/kotlin/com/flagship/abox/manager/ui/devicecontrol/DeviceControlComponents.kt deleted file mode 100644 index 593cd2b..0000000 --- a/android/app/src/main/kotlin/com/flagship/abox/manager/ui/devicecontrol/DeviceControlComponents.kt +++ /dev/null @@ -1,92 +0,0 @@ -package com.flagship.abox.manager.ui.devicecontrol - -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.ColumnScope -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.OutlinedButton -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.unit.dp -import com.flagship.abox.manager.designsystem.ABoxButton - -@Composable -internal fun DevicePageScaffold( - title: String, - description: String, - onBackClick: () -> Unit, - modifier: Modifier = Modifier, - content: @Composable ColumnScope.() -> Unit, -) { - Column( - modifier = modifier.padding(32.dp), - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.Center, - ) { - Text( - text = title, - style = MaterialTheme.typography.headlineLarge, - textAlign = TextAlign.Center, - ) - Text( - text = description, - modifier = Modifier.padding(top = 12.dp, bottom = 24.dp), - style = MaterialTheme.typography.bodyLarge, - textAlign = TextAlign.Center, - ) - content() - OutlinedButton( - onClick = onBackClick, - modifier = Modifier - .fillMaxWidth() - .padding(top = 24.dp), - ) { - Text("返回设备列表") - } - } -} - -@Composable -internal fun PrimaryActionButton( - text: String, - onClick: () -> Unit, - modifier: Modifier = Modifier, -) { - ABoxButton( - onClick = onClick, - modifier = modifier.fillMaxWidth(), - ) { - Text(text) - } -} - -@Composable -internal fun TwoColumnActions( - leftText: String, - rightText: String, - onLeftClick: () -> Unit, - onRightClick: () -> Unit, - modifier: Modifier = Modifier, -) { - Row( - modifier = modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.spacedBy(16.dp), - ) { - PrimaryActionButton( - text = leftText, - onClick = onLeftClick, - modifier = Modifier.weight(1f), - ) - PrimaryActionButton( - text = rightText, - onClick = onRightClick, - modifier = Modifier.weight(1f), - ) - } -} diff --git a/android/app/src/main/kotlin/com/flagship/abox/manager/ui/devicecontrol/DeviceControlRoutes.kt b/android/app/src/main/kotlin/com/flagship/abox/manager/ui/devicecontrol/DeviceControlRoutes.kt deleted file mode 100644 index 71b5a56..0000000 --- a/android/app/src/main/kotlin/com/flagship/abox/manager/ui/devicecontrol/DeviceControlRoutes.kt +++ /dev/null @@ -1,29 +0,0 @@ -package com.flagship.abox.manager.ui.devicecontrol - -import androidx.navigation.NavGraphBuilder -import androidx.navigation.NavHostController -import androidx.navigation.compose.composable - -const val DEVICE_LIST_ROUTE = "device_list" -private const val SOCKET_ROUTE = "device_socket" -private const val TH_ROUTE = "device_temperature_humidity" -private const val INFRARED_ROUTE = "device_infrared" - -fun NavGraphBuilder.deviceControlGraph(navController: NavHostController) { - composable(DEVICE_LIST_ROUTE) { - DeviceListScreen( - onSocketClick = { navController.navigate(SOCKET_ROUTE) }, - onTemperatureHumidityClick = { navController.navigate(TH_ROUTE) }, - onInfraredClick = { navController.navigate(INFRARED_ROUTE) }, - ) - } - composable(SOCKET_ROUTE) { - SocketControlScreen(onBackClick = navController::popBackStack) - } - composable(TH_ROUTE) { - TemperatureHumidityScreen(onBackClick = navController::popBackStack) - } - composable(INFRARED_ROUTE) { - InfraredControlScreen(onBackClick = navController::popBackStack) - } -} diff --git a/android/app/src/main/kotlin/com/flagship/abox/manager/ui/devicecontrol/InfraredControlScreen.kt b/android/app/src/main/kotlin/com/flagship/abox/manager/ui/devicecontrol/InfraredControlScreen.kt deleted file mode 100644 index 8d06d7f..0000000 --- a/android/app/src/main/kotlin/com/flagship/abox/manager/ui/devicecontrol/InfraredControlScreen.kt +++ /dev/null @@ -1,75 +0,0 @@ -package com.flagship.abox.manager.ui.devicecontrol - -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.OutlinedTextField -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue -import androidx.compose.ui.Modifier -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.unit.dp - -@Composable -fun InfraredControlScreen( - onBackClick: () -> Unit, - modifier: Modifier = Modifier, -) { - var deviceName by remember { mutableStateOf("红外遥控器") } - var key by remember { mutableStateOf("") } - var statusText by remember { mutableStateOf("输入设备名称和按键 KEY 后执行红外操作") } - - DevicePageScaffold( - title = "红外遥控器", - description = "发送红外按键或学习新按键。后续可接入 InfraredGateway。", - onBackClick = onBackClick, - modifier = modifier.fillMaxSize(), - ) { - OutlinedTextField( - value = deviceName, - onValueChange = { deviceName = it }, - modifier = Modifier.fillMaxWidth(), - label = { Text("红外设备名称") }, - singleLine = true, - ) - OutlinedTextField( - value = key, - onValueChange = { key = it }, - modifier = Modifier - .fillMaxWidth() - .padding(top = 16.dp), - label = { Text("按键 KEY,例如 POWER") }, - singleLine = true, - ) - TwoColumnActions( - leftText = "发送", - rightText = "学习", - onLeftClick = { - statusText = if (key.isBlank()) { - "请先输入按键 KEY。" - } else { - "已请求发送 $key(待接入真实红外发送)" - } - }, - onRightClick = { - statusText = if (key.isBlank()) { - "请先输入按键 KEY。" - } else { - "已请求学习 $key(待接入真实红外学习)" - } - }, - modifier = Modifier.padding(top = 24.dp), - ) - Text( - text = statusText, - modifier = Modifier.padding(top = 20.dp), - style = MaterialTheme.typography.bodyMedium, - textAlign = TextAlign.Center, - ) - } -} diff --git a/android/app/src/main/kotlin/com/flagship/abox/manager/ui/devicecontrol/SocketControlScreen.kt b/android/app/src/main/kotlin/com/flagship/abox/manager/ui/devicecontrol/SocketControlScreen.kt deleted file mode 100644 index b5e966f..0000000 --- a/android/app/src/main/kotlin/com/flagship/abox/manager/ui/devicecontrol/SocketControlScreen.kt +++ /dev/null @@ -1,60 +0,0 @@ -package com.flagship.abox.manager.ui.devicecontrol - -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.padding -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue -import androidx.compose.ui.Modifier -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.unit.dp - -@Composable -fun SocketControlScreen( - onBackClick: () -> Unit, - modifier: Modifier = Modifier, -) { - var statusText by remember { mutableStateOf("当前状态:未知") } - - DevicePageScaffold( - title = "智能插座", - description = "Kotlin Compose 版插座控制页面。后续可接入 SocketGateway。", - onBackClick = onBackClick, - modifier = modifier.fillMaxSize(), - ) { - Text( - text = statusText, - modifier = Modifier.padding(bottom = 24.dp), - style = MaterialTheme.typography.headlineSmall, - textAlign = TextAlign.Center, - ) - PrimaryActionButton( - text = "刷新状态", - onClick = { - statusText = "当前状态:等待接入真实状态查询" - }, - ) - TwoColumnActions( - leftText = "打开", - rightText = "关闭", - onLeftClick = { - statusText = "当前状态:已请求打开(待接入真实控制)" - }, - onRightClick = { - statusText = "当前状态:已请求关闭(待接入真实控制)" - }, - modifier = Modifier.padding(top = 16.dp), - ) - Text( - text = "建议后续调用 SocketGateway.getStatus / setPower,而不是直接调用 ABSDK。", - modifier = Modifier.padding(top = 20.dp), - style = MaterialTheme.typography.bodyMedium, - textAlign = TextAlign.Center, - ) - } -} diff --git a/android/app/src/main/kotlin/com/flagship/abox/manager/ui/devicecontrol/TemperatureHumidityScreen.kt b/android/app/src/main/kotlin/com/flagship/abox/manager/ui/devicecontrol/TemperatureHumidityScreen.kt deleted file mode 100644 index 18508db..0000000 --- a/android/app/src/main/kotlin/com/flagship/abox/manager/ui/devicecontrol/TemperatureHumidityScreen.kt +++ /dev/null @@ -1,57 +0,0 @@ -package com.flagship.abox.manager.ui.devicecontrol - -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.padding -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue -import androidx.compose.ui.Modifier -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.unit.dp - -@Composable -fun TemperatureHumidityScreen( - onBackClick: () -> Unit, - modifier: Modifier = Modifier, -) { - var temperature by remember { mutableStateOf("-- ℃") } - var humidity by remember { mutableStateOf("-- %") } - var statusText by remember { mutableStateOf("点击按钮查询当前温湿度") } - - DevicePageScaffold( - title = "温湿度状态", - description = "显示温度与湿度读数。后续可接入 TemperatureHumidityGateway。", - onBackClick = onBackClick, - modifier = modifier.fillMaxSize(), - ) { - Text( - text = "温度:$temperature", - style = MaterialTheme.typography.headlineSmall, - textAlign = TextAlign.Center, - ) - Text( - text = "湿度:$humidity", - modifier = Modifier.padding(top = 12.dp, bottom = 24.dp), - style = MaterialTheme.typography.headlineSmall, - textAlign = TextAlign.Center, - ) - PrimaryActionButton( - text = "刷新温湿度", - onClick = { - temperature = "待接入" - humidity = "待接入" - statusText = "已触发刷新动作,等待接入真实 ABSDK bridge。" - }, - ) - Text( - text = statusText, - modifier = Modifier.padding(top = 20.dp), - style = MaterialTheme.typography.bodyMedium, - textAlign = TextAlign.Center, - ) - } -} diff --git a/android/app/src/main/kotlin/com/flagship/abox/manager/ui/devicecontrol/DeviceListScreen.kt b/android/app/src/main/kotlin/com/flagship/abox/manager/ui/devicecontrollist/DeviceListScreen.kt similarity index 88% rename from android/app/src/main/kotlin/com/flagship/abox/manager/ui/devicecontrol/DeviceListScreen.kt rename to android/app/src/main/kotlin/com/flagship/abox/manager/ui/devicecontrollist/DeviceListScreen.kt index a710c94..c398b25 100644 --- a/android/app/src/main/kotlin/com/flagship/abox/manager/ui/devicecontrol/DeviceListScreen.kt +++ b/android/app/src/main/kotlin/com/flagship/abox/manager/ui/devicecontrollist/DeviceListScreen.kt @@ -1,4 +1,4 @@ -package com.flagship.abox.manager.ui.devicecontrol +package com.flagship.abox.manager.ui.devicecontrollist import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column @@ -41,17 +41,17 @@ fun DeviceListScreen( ) DeviceEntryButton( text = "智能插座", - description = "查看插座状态,执行打开或关闭操作。", + description = "进入智能插座相关功能。", onClick = onSocketClick, ) DeviceEntryButton( text = "温湿度传感器", - description = "查询当前温度与湿度读数。", + description = "进入温湿度状态相关功能。", onClick = onTemperatureHumidityClick, ) DeviceEntryButton( - text = "红外遥控器", - description = "发送红外按键,或进入红外学习流程。", + text = "红外控制器", + description = "进入红外控制器相关功能。", onClick = onInfraredClick, ) } diff --git a/docs/device-control-compose-ui-change-log.md b/docs/device-control-compose-ui-change-log.md index bfefaec..9cc1428 100644 --- a/docs/device-control-compose-ui-change-log.md +++ b/docs/device-control-compose-ui-change-log.md @@ -1,114 +1,51 @@ -# Device Control Compose UI Change Log +# Device List Compose UI Change Log Branch: `feature/device-control-compose-ui` ## Summary -This branch adds a Kotlin Jetpack Compose implementation for the device-control UI owned by this feature area. +This branch adds only the Kotlin Jetpack Compose device list page owned by this feature area. -The implementation introduces a new device-control entry point and a group of Compose screens for the three device types required by the current project scope: - -- Socket device control -- Temperature/humidity status display -- Infrared remote control actions - -The existing placeholder/foundation app flow is preserved. The new device-control flow is added as an additional navigation path instead of replacing the existing screens. - -## Modified Files - -### `android/app/src/main/kotlin/com/flagship/abox/manager/ui/ABoxManagerApp.kt` - -Changes: - -- Added an entry button on the foundation screen for the Kotlin device-control UI. -- Registered the new device-control navigation graph in the existing `NavHost`. -- Preserved the existing foundation and placeholder routes. - -Purpose: - -- Allows teammates to open the newly added device-control screens from the current Compose app shell. - -## Added Files - -### `android/app/src/main/kotlin/com/flagship/abox/manager/ui/devicecontrol/DeviceControlRoutes.kt` - -Adds navigation routes and a small navigation graph for the device-control feature. - -Routes included: - -- Device list -- Socket control -- Temperature/humidity status -- Infrared control - -### `android/app/src/main/kotlin/com/flagship/abox/manager/ui/devicecontrol/DeviceListScreen.kt` - -Adds the top-level device list screen. - -The screen contains entries for: +The implementation provides a reusable Compose screen that displays the ABox terminal device categories: - Smart socket - Temperature/humidity sensor -- Infrared remote controller +- Infrared controller -Each entry navigates to its corresponding Compose screen. +This branch intentionally does not modify the app entry point or app shell routing. The app shell, login flow, and detail-feature routing are owned by other feature branches. -### `android/app/src/main/kotlin/com/flagship/abox/manager/ui/devicecontrol/DeviceControlComponents.kt` +## Added Files -Adds shared UI components used by the device-control screens. +### `android/app/src/main/kotlin/com/flagship/abox/manager/ui/devicecontrollist/DeviceListScreen.kt` -Included components: +Adds the device list page. -- Shared device page scaffold -- Primary action button wrapper -- Two-column action button row +The page displays entries for: -Purpose: - -- Keeps repeated screen layout and action button patterns consistent across device screens. - -### `android/app/src/main/kotlin/com/flagship/abox/manager/ui/devicecontrol/SocketControlScreen.kt` - -Adds a Compose screen for socket control. +- Smart socket +- Temperature/humidity sensor +- Infrared controller Current scope: -- Displays socket status text. -- Provides actions for refreshing status, turning on, and turning off. -- Uses local Compose state as a placeholder. +- Display the device categories as list entry UI. +- Keep the UI in Kotlin Compose. +- Expose click callbacks for each device category. +- Avoid adding app-shell routing, detail routes, or detail control pages in this branch. + +Integration API: + +```kotlin +DeviceListScreen( + onSocketClick = { /* route to socket module later */ }, + onTemperatureHumidityClick = { /* route to temperature/humidity module later */ }, + onInfraredClick = { /* route to infrared controller module later */ }, +) +``` Future integration point: -- Replace placeholder state handling with `SocketGateway.getStatus(...)` and `SocketGateway.setPower(...)` through a ViewModel. - -### `android/app/src/main/kotlin/com/flagship/abox/manager/ui/devicecontrol/TemperatureHumidityScreen.kt` - -Adds a Compose screen for temperature/humidity display. - -Current scope: - -- Displays temperature and humidity values. -- Provides a refresh action. -- Uses local Compose state as a placeholder. - -Future integration point: - -- Replace placeholder state handling with `TemperatureHumidityGateway.getReading(...)` through a ViewModel. - -### `android/app/src/main/kotlin/com/flagship/abox/manager/ui/devicecontrol/InfraredControlScreen.kt` - -Adds a Compose screen for infrared remote operations. - -Current scope: - -- Allows entering an infrared device name. -- Allows entering an infrared key name. -- Provides send and learn actions. -- Uses local Compose state as a placeholder. - -Future integration point: - -- Replace placeholder state handling with `InfraredGateway.sendKey(...)` and `InfraredGateway.learnKey(...)` through a ViewModel. +- The app shell or feature owner responsible for routing can host this screen and connect these callbacks to their own routes/modules later. ## Architecture Notes @@ -117,33 +54,22 @@ This branch follows the current repository direction: - Kotlin instead of Java - Jetpack Compose instead of XML layouts - Single-Activity UI instead of multiple Activities -- Navigation Compose instead of `Intent`-based Activity navigation +- Navigation Compose compatibility through external callbacks - Existing design-system wrappers where practical This branch intentionally does not copy the previous Java/XML experiment module into the team repository. -## ABSDK Integration Notes - -The UI screens added in this branch do not call ABSDK directly. - -The repository already contains domain abstractions and an ABSDK bridge: - -- `SocketGateway` -- `TemperatureHumidityGateway` -- `InfraredGateway` -- `AbsdkBridge` -- `VendorAbsdkClient` - -Recommended next step: - -- Add ViewModels for the device-control screens. -- Inject/use the gateway interfaces from the domain layer. -- Expose screen UI state from ViewModels to the Compose screens. - ## Scope Intentionally Not Included This branch does not include: +- App entry point changes +- App shell or login routing changes +- A device-control navigation graph file +- Socket control screen implementation +- Temperature/humidity status screen implementation +- Infrared controller screen implementation +- Shared detail-control component scaffolding - Direct ABSDK calls from Compose UI - New Java Activity classes - XML layout files @@ -151,138 +77,74 @@ This branch does not include: - Full session/login integration for device gateway calls - Persistent infrared key storage -These should be added in later branches or follow-up commits after team architecture review. +These should be added by the corresponding feature owners in later branches or follow-up commits after team architecture review. + +## Verification + +- Built and ran `android.app` from Android Studio successfully during local validation. +- `:android:app:assembleDebug` completed successfully after trimming the feature scope. ## Files Intended for Commit The intended commit scope is limited to: ```text -android/app/src/main/kotlin/com/flagship/abox/manager/ui/ABoxManagerApp.kt -android/app/src/main/kotlin/com/flagship/abox/manager/ui/devicecontrol/ +android/app/src/main/kotlin/com/flagship/abox/manager/ui/devicecontrollist/DeviceListScreen.kt docs/device-control-compose-ui-change-log.md ``` -`settings.gradle.kts` is intentionally not part of this feature commit unless the team decides the local toolchain resolver change is required. +`settings.gradle.kts` is intentionally excluded from this feature commit. Local Gradle/JDK/toolchain setup should be handled on the developer machine and should not modify the team-provided project configuration in this branch. --- -# 设备控制 Compose UI 修改说明(中文) +# 设备列表 Compose UI 修改说明(中文) 分支:`feature/device-control-compose-ui` ## 概要 -本分支新增了一套 Kotlin Jetpack Compose 版本的设备控制 UI,用于承载当前负责的设备列表与三类终端设备页面。 +本分支只新增当前分工负责的 Kotlin Jetpack Compose 设备列表页面。 -本次新增的设备范围包括: - -- 智能插座控制 -- 温湿度状态显示 -- 红外遥控器操作 - -本分支没有替换原有的 foundation / placeholder 页面,而是在现有 Compose 应用壳中新增了一个设备控制入口。因此,原有占位流程仍然保留,设备控制流程是额外新增的导航路径。 - -## 修改文件 - -### `android/app/src/main/kotlin/com/flagship/abox/manager/ui/ABoxManagerApp.kt` - -修改内容: - -- 在 foundation 页面增加了一个进入 Kotlin 版设备控制 UI 的按钮。 -- 在现有 `NavHost` 中注册了设备控制导航图。 -- 保留了原有 foundation route 和 placeholder route。 - -作用: - -- 方便团队成员从当前 Compose 应用入口进入新增加的设备控制页面。 - -## 新增文件 - -### `android/app/src/main/kotlin/com/flagship/abox/manager/ui/devicecontrol/DeviceControlRoutes.kt` - -新增设备控制相关的 Compose Navigation 路由与导航图。 - -包含的页面路由: - -- 设备列表 -- 智能插座控制 -- 温湿度状态 -- 红外遥控器控制 - -### `android/app/src/main/kotlin/com/flagship/abox/manager/ui/devicecontrol/DeviceListScreen.kt` - -新增设备列表页面。 - -页面包含三个设备入口: +设备列表展示的设备类别包括: - 智能插座 - 温湿度传感器 -- 红外遥控器 +- 红外控制器 -每个入口会跳转到对应的 Compose 设备页面。 +本分支故意不修改 App 入口或应用壳路由。程序入口、登录流程和各设备详情模块路由由其他功能分支负责。 -### `android/app/src/main/kotlin/com/flagship/abox/manager/ui/devicecontrol/DeviceControlComponents.kt` +## 新增文件 -新增设备控制页面共用 UI 组件。 +### `android/app/src/main/kotlin/com/flagship/abox/manager/ui/devicecontrollist/DeviceListScreen.kt` -包含: +新增设备列表页面。 -- 通用设备页面脚手架 -- 主要操作按钮封装 -- 双按钮横向操作区 +页面展示三个设备入口: -作用: - -- 减少各设备页面中的重复布局代码。 -- 让插座、温湿度、红外页面保持相似的视觉结构。 - -### `android/app/src/main/kotlin/com/flagship/abox/manager/ui/devicecontrol/SocketControlScreen.kt` - -新增智能插座控制页面。 +- 智能插座 +- 温湿度传感器 +- 红外控制器 当前实现范围: -- 显示插座状态文本。 -- 提供刷新状态、打开、关闭三个操作入口。 -- 当前使用本地 Compose state 作为占位状态。 +- 展示设备类别入口 UI。 +- 保持 Kotlin Compose 实现。 +- 为每个设备类别暴露点击回调。 +- 不在本分支实现 App 入口接入、设备详情 route 或设备控制页面。 + +对接 API: + +```kotlin +DeviceListScreen( + onSocketClick = { /* 后续跳转到智能插座模块 */ }, + onTemperatureHumidityClick = { /* 后续跳转到温湿度模块 */ }, + onInfraredClick = { /* 后续跳转到红外控制器模块 */ }, +) +``` 后续接入点: -- 通过 ViewModel 接入 `SocketGateway.getStatus(...)`。 -- 通过 ViewModel 接入 `SocketGateway.setPower(...)`。 - -### `android/app/src/main/kotlin/com/flagship/abox/manager/ui/devicecontrol/TemperatureHumidityScreen.kt` - -新增温湿度状态页面。 - -当前实现范围: - -- 显示温度值。 -- 显示湿度值。 -- 提供刷新温湿度按钮。 -- 当前使用本地 Compose state 作为占位状态。 - -后续接入点: - -- 通过 ViewModel 接入 `TemperatureHumidityGateway.getReading(...)`。 - -### `android/app/src/main/kotlin/com/flagship/abox/manager/ui/devicecontrol/InfraredControlScreen.kt` - -新增红外遥控器页面。 - -当前实现范围: - -- 输入红外设备名称。 -- 输入红外按键 KEY。 -- 提供发送红外按键操作。 -- 提供学习红外按键操作。 -- 当前使用本地 Compose state 作为占位状态。 - -后续接入点: - -- 通过 ViewModel 接入 `InfraredGateway.sendKey(...)`。 -- 通过 ViewModel 接入 `InfraredGateway.learnKey(...)`。 +- 由负责 App shell 或对应设备模块的同学,在各自分支中挂载这个页面,并把这些回调连接到他们自己的详情路由或功能模块。 ## 架构说明 @@ -291,37 +153,22 @@ docs/device-control-compose-ui-change-log.md - 使用 Kotlin,而不是 Java。 - 使用 Jetpack Compose,而不是 XML layout。 - 使用单 Activity 架构,而不是多个 Activity 跳转。 -- 使用 Navigation Compose,而不是 `Intent` 页面跳转。 +- 通过外部回调保持与 Navigation Compose 的兼容性。 - 尽量复用已有 design system 中的组件封装。 本分支没有把之前个人实验项目中的 Java/XML 模块直接复制进团队仓库。 -## ABSDK 接入说明 - -本次新增的 Compose UI 页面没有直接调用 ABSDK。 - -当前仓库中已经存在较正式的 domain 抽象和 ABSDK bridge: - -- `SocketGateway` -- `TemperatureHumidityGateway` -- `InfraredGateway` -- `AbsdkBridge` -- `VendorAbsdkClient` - -因此后续真实设备控制逻辑建议通过 ViewModel 调用 domain gateway,而不是在 Compose UI 中直接调用 ABSDK。 - -推荐后续工作: - -- 为插座页面新增 ViewModel。 -- 为温湿度页面新增 ViewModel。 -- 为红外页面新增 ViewModel。 -- 在 ViewModel 中调用对应 gateway。 -- 将调用结果转换为 Compose UI state。 - ## 本分支未包含的内容 本分支暂不包含: +- App 入口修改。 +- App shell 或登录路由修改。 +- 设备控制 navigation graph 文件。 +- 智能插座控制页面。 +- 温湿度状态页面。 +- 红外控制器操作页面。 +- 设备详情控制页面共用组件。 - Compose UI 中直接调用 ABSDK。 - 新增 Java Activity。 - 新增 XML layout。 @@ -329,16 +176,20 @@ docs/device-control-compose-ui-change-log.md - 完整登录/session 与设备 gateway 的联动。 - 红外学习按键的持久化存储。 -这些内容建议在团队确认架构之后,作为后续 branch 或后续 commit 继续实现。 +这些内容建议由对应模块负责人在后续 branch 或后续 commit 中继续实现。 + +## 验证 + +- 本地验证过程中已从 Android Studio 成功 build/run `android.app`。 +- 修剪职责范围后,`:android:app:assembleDebug` 构建成功。 ## 预期提交范围 本次提交范围应限制在: ```text -android/app/src/main/kotlin/com/flagship/abox/manager/ui/ABoxManagerApp.kt -android/app/src/main/kotlin/com/flagship/abox/manager/ui/devicecontrol/ +android/app/src/main/kotlin/com/flagship/abox/manager/ui/devicecontrollist/DeviceListScreen.kt docs/device-control-compose-ui-change-log.md ``` -`settings.gradle.kts` 当前不属于本次设备控制 UI 功能提交范围,除非团队明确认为本地 toolchain resolver 配置也需要进入 PR。 +`settings.gradle.kts` 当前不属于本次设备列表 UI 功能提交范围。本地 Gradle/JDK/toolchain 配置应在开发者本机处理,不应修改团队提供的项目配置文件。 -- 2.54.0 From 0b9aab365e827c4c546413b882380ef3341152e5 Mon Sep 17 00:00:00 2001 From: alZerNest <2223976172@qq.com> Date: Tue, 25 Aug 2026 09:27:39 +0800 Subject: [PATCH 3/4] Address device list review feedback --- .../ui/devicecontrollist/DeviceListScreen.kt | 17 +++ docs/device-list-compose-ui-pr-description.md | 133 ++++++++++++++++++ 2 files changed, 150 insertions(+) create mode 100644 docs/device-list-compose-ui-pr-description.md diff --git a/android/app/src/main/kotlin/com/flagship/abox/manager/ui/devicecontrollist/DeviceListScreen.kt b/android/app/src/main/kotlin/com/flagship/abox/manager/ui/devicecontrollist/DeviceListScreen.kt index c398b25..32c51d9 100644 --- a/android/app/src/main/kotlin/com/flagship/abox/manager/ui/devicecontrollist/DeviceListScreen.kt +++ b/android/app/src/main/kotlin/com/flagship/abox/manager/ui/devicecontrollist/DeviceListScreen.kt @@ -5,14 +5,18 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.flagship.abox.manager.designsystem.ABoxButton +import com.flagship.abox.manager.designsystem.ABoxTheme @Composable fun DeviceListScreen( @@ -24,6 +28,7 @@ fun DeviceListScreen( Column( modifier = modifier .fillMaxSize() + .verticalScroll(rememberScrollState()) .padding(32.dp), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center, @@ -78,3 +83,15 @@ private fun DeviceEntryButton( } } } + +@Preview(showBackground = true) +@Composable +private fun DeviceListScreenPreview() { + ABoxTheme { + DeviceListScreen( + onSocketClick = {}, + onTemperatureHumidityClick = {}, + onInfraredClick = {}, + ) + } +} diff --git a/docs/device-list-compose-ui-pr-description.md b/docs/device-list-compose-ui-pr-description.md new file mode 100644 index 0000000..28d50e7 --- /dev/null +++ b/docs/device-list-compose-ui-pr-description.md @@ -0,0 +1,133 @@ +# WIP: Add device list Compose screen + +## Summary + +This MR adds only the reusable Kotlin Jetpack Compose device list screen for my assigned scope. + +The screen displays the three ABox device categories: + +- Smart socket +- Temperature/humidity sensor +- Infrared controller + +This MR intentionally does not modify the app entry point, login flow, app shell routing, or any device detail/control screen. Those parts are handled by other feature branches. + +## Changes + +- Add `DeviceListScreen`. +- Expose click callbacks for each device category: + - `onSocketClick` + - `onTemperatureHumidityClick` + - `onInfraredClick` +- Update the device-list change log document. +- Remove the earlier out-of-scope temporary device control screens from this branch. + +## Scope + +Included: + +- Device list Compose UI component +- Device category entry UI +- Callback API for future route integration + +Not included: + +- App entry point changes +- Login flow changes +- Device control navigation graph +- Smart socket control screen +- Temperature/humidity status screen +- Infrared controller screen +- Direct ABSDK calls +- Java Activity classes +- XML layout files + +## Integration Notes + +The screen is designed to be hosted by the app shell or routing owner later: + +```kotlin +DeviceListScreen( + onSocketClick = { /* route to socket module */ }, + onTemperatureHumidityClick = { /* route to temperature/humidity module */ }, + onInfraredClick = { /* route to infrared controller module */ }, +) +``` + +The current branch keeps these callbacks external so other feature owners can connect their modules without this MR taking ownership of routing. + +## Verification + +- Built and ran `android.app` locally from Android Studio. +- `:android:app:assembleDebug` completed successfully after trimming this branch to the device-list component scope. + +## Notes + +`settings.gradle.kts` is intentionally not part of this MR. Local Gradle/JDK/toolchain setup is handled on the developer machine and should not modify the team-provided project configuration. + +--- + +# WIP: 新增设备列表 Compose 页面 + +## 概要 + +本 MR 只新增我当前分工负责的 Kotlin Jetpack Compose 设备列表页面组件。 + +设备列表展示三类 ABox 设备入口: + +- 智能插座 +- 温湿度传感器 +- 红外控制器 + +本 MR 故意不修改 App 入口、登录流程、应用壳路由,也不实现任何设备详情/控制页面。这些部分由其他功能分支负责。 + +## 修改内容 + +- 新增 `DeviceListScreen`。 +- 为三类设备入口暴露点击回调: + - `onSocketClick` + - `onTemperatureHumidityClick` + - `onInfraredClick` +- 更新设备列表变更说明文档。 +- 从本分支中移除早期临时加入、但不属于当前分工范围的设备控制页面。 + +## 本 MR 包含 + +- 设备列表 Compose UI 组件 +- 设备类别入口 UI +- 后续路由接入所需的 callback API + +## 本 MR 不包含 + +- App 入口修改 +- 登录流程修改 +- 设备控制 navigation graph +- 智能插座控制页面 +- 温湿度状态页面 +- 红外控制器页面 +- Compose UI 中直接调用 ABSDK +- Java Activity +- XML layout + +## 对接说明 + +该页面后续可以由 App shell 或路由负责人挂载: + +```kotlin +DeviceListScreen( + onSocketClick = { /* 后续跳转到智能插座模块 */ }, + onTemperatureHumidityClick = { /* 后续跳转到温湿度模块 */ }, + onInfraredClick = { /* 后续跳转到红外控制器模块 */ }, +) +``` + +当前分支只提供页面和回调 API,不负责具体跳转目标,以便其他模块负责人对接自己的功能页面。 + +## 验证 + +- 已在本地通过 Android Studio 成功 build/run `android.app`。 +- 修剪为设备列表组件范围后,`:android:app:assembleDebug` 构建成功。 + +## 备注 + +`settings.gradle.kts` 不属于本 MR 范围。本地 Gradle/JDK/toolchain 配置在开发者本机处理,不修改团队提供的项目配置文件。 -- 2.54.0 From ae488ef8c38d36b16bc894ee0945588f4ee6f919 Mon Sep 17 00:00:00 2001 From: KremeCN <3265494849@qq.com> Date: Tue, 25 Aug 2026 11:27:41 +0800 Subject: [PATCH 4/4] feat(devices): connect DeviceListScreen to MainAppShell devicesSlot - Wire DeviceListScreen as default composable in MainAppShell devicesSlot - Extract and localize device list strings in en and zh-rCN - Add DeviceListScreenTest verifying rendering and click callbacks --- .../devicecontrollist/DeviceListScreenTest.kt | 54 +++++++++++++++++++ .../ui/devicecontrollist/DeviceListScreen.kt | 30 +++++------ .../abox/manager/ui/shell/MainAppShell.kt | 11 +--- .../src/main/res/values-zh-rCN/strings.xml | 10 ++++ android/app/src/main/res/values/strings.xml | 10 ++++ 5 files changed, 90 insertions(+), 25 deletions(-) create mode 100644 android/app/src/androidTest/kotlin/com/flagship/abox/manager/ui/devicecontrollist/DeviceListScreenTest.kt diff --git a/android/app/src/androidTest/kotlin/com/flagship/abox/manager/ui/devicecontrollist/DeviceListScreenTest.kt b/android/app/src/androidTest/kotlin/com/flagship/abox/manager/ui/devicecontrollist/DeviceListScreenTest.kt new file mode 100644 index 0000000..4db7a3b --- /dev/null +++ b/android/app/src/androidTest/kotlin/com/flagship/abox/manager/ui/devicecontrollist/DeviceListScreenTest.kt @@ -0,0 +1,54 @@ +package com.flagship.abox.manager.ui.devicecontrollist + +import androidx.activity.ComponentActivity +import androidx.compose.ui.test.assertIsDisplayed +import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.onNodeWithText +import androidx.compose.ui.test.performClick +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.flagship.abox.manager.R +import com.flagship.abox.manager.designsystem.ABoxTheme +import org.junit.Assert.assertEquals +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class DeviceListScreenTest { + @get:Rule + val composeRule = createAndroidComposeRule() + + @Test + fun deviceListRendersAllDeviceCategoryButtonsAndHandlesClicks() { + var socketClicks = 0 + var tempClicks = 0 + var infraredClicks = 0 + + val context = composeRule.activity + val socketTitle = context.getString(R.string.device_socket_title) + val tempTitle = context.getString(R.string.device_temperature_humidity_title) + val infraredTitle = context.getString(R.string.device_infrared_title) + val listTitle = context.getString(R.string.device_list_title) + + composeRule.setContent { + ABoxTheme { + DeviceListScreen( + onSocketClick = { socketClicks++ }, + onTemperatureHumidityClick = { tempClicks++ }, + onInfraredClick = { infraredClicks++ }, + ) + } + } + + composeRule.onNodeWithText(listTitle).assertIsDisplayed() + + composeRule.onNodeWithText(socketTitle).assertIsDisplayed().performClick() + assertEquals(1, socketClicks) + + composeRule.onNodeWithText(tempTitle).assertIsDisplayed().performClick() + assertEquals(1, tempClicks) + + composeRule.onNodeWithText(infraredTitle).assertIsDisplayed().performClick() + assertEquals(1, infraredClicks) + } +} diff --git a/android/app/src/main/kotlin/com/flagship/abox/manager/ui/devicecontrollist/DeviceListScreen.kt b/android/app/src/main/kotlin/com/flagship/abox/manager/ui/devicecontrollist/DeviceListScreen.kt index 32c51d9..2dfadcc 100644 --- a/android/app/src/main/kotlin/com/flagship/abox/manager/ui/devicecontrollist/DeviceListScreen.kt +++ b/android/app/src/main/kotlin/com/flagship/abox/manager/ui/devicecontrollist/DeviceListScreen.kt @@ -12,18 +12,20 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import com.flagship.abox.manager.R import com.flagship.abox.manager.designsystem.ABoxButton import com.flagship.abox.manager.designsystem.ABoxTheme @Composable fun DeviceListScreen( - onSocketClick: () -> Unit, - onTemperatureHumidityClick: () -> Unit, - onInfraredClick: () -> Unit, modifier: Modifier = Modifier, + onSocketClick: () -> Unit = {}, + onTemperatureHumidityClick: () -> Unit = {}, + onInfraredClick: () -> Unit = {}, ) { Column( modifier = modifier @@ -34,29 +36,29 @@ fun DeviceListScreen( verticalArrangement = Arrangement.Center, ) { Text( - text = "设备列表", + text = stringResource(R.string.device_list_title), style = MaterialTheme.typography.headlineLarge, textAlign = TextAlign.Center, ) Text( - text = "选择需要控制或查看状态的 ABox 终端设备。", + text = stringResource(R.string.device_list_description), modifier = Modifier.padding(top = 12.dp, bottom = 32.dp), style = MaterialTheme.typography.bodyLarge, textAlign = TextAlign.Center, ) DeviceEntryButton( - text = "智能插座", - description = "进入智能插座相关功能。", + text = stringResource(R.string.device_socket_title), + description = stringResource(R.string.device_socket_description), onClick = onSocketClick, ) DeviceEntryButton( - text = "温湿度传感器", - description = "进入温湿度状态相关功能。", + text = stringResource(R.string.device_temperature_humidity_title), + description = stringResource(R.string.device_temperature_humidity_description), onClick = onTemperatureHumidityClick, ) DeviceEntryButton( - text = "红外控制器", - description = "进入红外控制器相关功能。", + text = stringResource(R.string.device_infrared_title), + description = stringResource(R.string.device_infrared_description), onClick = onInfraredClick, ) } @@ -88,10 +90,6 @@ private fun DeviceEntryButton( @Composable private fun DeviceListScreenPreview() { ABoxTheme { - DeviceListScreen( - onSocketClick = {}, - onTemperatureHumidityClick = {}, - onInfraredClick = {}, - ) + DeviceListScreen() } } diff --git a/android/app/src/main/kotlin/com/flagship/abox/manager/ui/shell/MainAppShell.kt b/android/app/src/main/kotlin/com/flagship/abox/manager/ui/shell/MainAppShell.kt index 6c2c8ce..d32ebdc 100644 --- a/android/app/src/main/kotlin/com/flagship/abox/manager/ui/shell/MainAppShell.kt +++ b/android/app/src/main/kotlin/com/flagship/abox/manager/ui/shell/MainAppShell.kt @@ -29,6 +29,7 @@ import com.flagship.abox.manager.R import com.flagship.abox.manager.designsystem.ABoxNavigationBar import com.flagship.abox.manager.designsystem.ABoxNavigationBarItem import com.flagship.abox.manager.designsystem.ABoxTopPillBar +import com.flagship.abox.manager.ui.devicecontrollist.DeviceListScreen /** * Main authenticated application shell. @@ -50,7 +51,7 @@ fun MainAppShell( onAddClick: (() -> Unit)? = null, actionInProgress: Boolean = false, errorMessage: String? = null, - devicesSlot: @Composable () -> Unit = { DefaultDevicesSlot() }, + devicesSlot: @Composable () -> Unit = { DeviceListScreen() }, scenesSlot: @Composable () -> Unit = { DefaultScenesSlot() }, activitySlot: @Composable () -> Unit = { DefaultActivitySlot() }, ) { @@ -133,14 +134,6 @@ fun MainAppShell( } } -@Composable -private fun DefaultDevicesSlot() { - SlotPlaceholder( - title = stringResource(R.string.devices_placeholder_title), - description = stringResource(R.string.devices_placeholder_description), - ) -} - @Composable private fun DefaultScenesSlot() { SlotPlaceholder( diff --git a/android/app/src/main/res/values-zh-rCN/strings.xml b/android/app/src/main/res/values-zh-rCN/strings.xml index 696dc16..f09dfa4 100644 --- a/android/app/src/main/res/values-zh-rCN/strings.xml +++ b/android/app/src/main/res/values-zh-rCN/strings.xml @@ -42,4 +42,14 @@ 情景动作与执行插槽(由 tzh 模块实现)。 活动记录 未记录任何事件。 + + + 设备列表 + 选择需要控制或查看状态的 ABox 终端设备。 + 智能插座 + 进入智能插座相关功能。 + 温湿度传感器 + 进入温湿度状态相关功能。 + 红外控制器 + 进入红外控制器相关功能。 diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml index c8818bb..f9455e4 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml @@ -42,4 +42,14 @@ Scene actions and execution slot for tzh. Activity Log No recorded events yet. + + + Device List + Select an ABox terminal device to control or check status. + Smart Socket + Access smart socket features. + Temperature & Humidity Sensor + View temperature and humidity status. + Infrared Controller + Access infrared controller features. -- 2.54.0