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.
This commit is contained in:
2026-08-25 11:10:20 +08:00
committed by KremeCN
parent ed98c2be98
commit 594bfdd305
7 changed files with 737 additions and 0 deletions
@@ -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),
)
}
}
@@ -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)
}
}
@@ -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)
}
}
}
@@ -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,
)
}
}
@@ -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,
)
}
}
@@ -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,
)
}
}