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:
+92
@@ -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),
|
||||
)
|
||||
}
|
||||
}
|
||||
+29
@@ -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)
|
||||
}
|
||||
}
|
||||
+80
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
+75
@@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
+60
@@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
+57
@@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user