新增设备列表 Compose 页面 #8

Merged
KremeCN merged 4 commits from feature/device-control-compose-ui into main 2026-08-25 11:39:12 +08:00
5 changed files with 90 additions and 25 deletions
Showing only changes of commit ae488ef8c3 - Show all commits
@@ -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<ComponentActivity>()
@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)
}
}
@@ -12,18 +12,20 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp 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.ABoxButton
import com.flagship.abox.manager.designsystem.ABoxTheme import com.flagship.abox.manager.designsystem.ABoxTheme
@Composable @Composable
fun DeviceListScreen( fun DeviceListScreen(
onSocketClick: () -> Unit,
onTemperatureHumidityClick: () -> Unit,
onInfraredClick: () -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
onSocketClick: () -> Unit = {},
onTemperatureHumidityClick: () -> Unit = {},
onInfraredClick: () -> Unit = {},
) { ) {
Column( Column(
modifier = modifier modifier = modifier
@@ -34,29 +36,29 @@ fun DeviceListScreen(
verticalArrangement = Arrangement.Center, verticalArrangement = Arrangement.Center,
) { ) {
Text( Text(
text = "设备列表", text = stringResource(R.string.device_list_title),
style = MaterialTheme.typography.headlineLarge, style = MaterialTheme.typography.headlineLarge,
textAlign = TextAlign.Center, textAlign = TextAlign.Center,
) )
Text( Text(
text = "选择需要控制或查看状态的 ABox 终端设备。", text = stringResource(R.string.device_list_description),
modifier = Modifier.padding(top = 12.dp, bottom = 32.dp), modifier = Modifier.padding(top = 12.dp, bottom = 32.dp),
style = MaterialTheme.typography.bodyLarge, style = MaterialTheme.typography.bodyLarge,
textAlign = TextAlign.Center, textAlign = TextAlign.Center,
) )
DeviceEntryButton( DeviceEntryButton(
text = "智能插座", text = stringResource(R.string.device_socket_title),
description = "进入智能插座相关功能。", description = stringResource(R.string.device_socket_description),
onClick = onSocketClick, onClick = onSocketClick,
) )
DeviceEntryButton( DeviceEntryButton(
text = "温湿度传感器", text = stringResource(R.string.device_temperature_humidity_title),
description = "进入温湿度状态相关功能。", description = stringResource(R.string.device_temperature_humidity_description),
onClick = onTemperatureHumidityClick, onClick = onTemperatureHumidityClick,
) )
DeviceEntryButton( DeviceEntryButton(
text = "红外控制器", text = stringResource(R.string.device_infrared_title),
description = "进入红外控制器相关功能。", description = stringResource(R.string.device_infrared_description),
onClick = onInfraredClick, onClick = onInfraredClick,
) )
} }
@@ -88,10 +90,6 @@ private fun DeviceEntryButton(
@Composable @Composable
private fun DeviceListScreenPreview() { private fun DeviceListScreenPreview() {
ABoxTheme { ABoxTheme {
DeviceListScreen( DeviceListScreen()
onSocketClick = {},
onTemperatureHumidityClick = {},
onInfraredClick = {},
)
} }
} }
@@ -29,6 +29,7 @@ import com.flagship.abox.manager.R
import com.flagship.abox.manager.designsystem.ABoxNavigationBar import com.flagship.abox.manager.designsystem.ABoxNavigationBar
import com.flagship.abox.manager.designsystem.ABoxNavigationBarItem import com.flagship.abox.manager.designsystem.ABoxNavigationBarItem
import com.flagship.abox.manager.designsystem.ABoxTopPillBar import com.flagship.abox.manager.designsystem.ABoxTopPillBar
import com.flagship.abox.manager.ui.devicecontrollist.DeviceListScreen
/** /**
* Main authenticated application shell. * Main authenticated application shell.
@@ -50,7 +51,7 @@ fun MainAppShell(
onAddClick: (() -> Unit)? = null, onAddClick: (() -> Unit)? = null,
actionInProgress: Boolean = false, actionInProgress: Boolean = false,
errorMessage: String? = null, errorMessage: String? = null,
devicesSlot: @Composable () -> Unit = { DefaultDevicesSlot() }, devicesSlot: @Composable () -> Unit = { DeviceListScreen() },
scenesSlot: @Composable () -> Unit = { DefaultScenesSlot() }, scenesSlot: @Composable () -> Unit = { DefaultScenesSlot() },
Review

无效入口

MainAppShell 默认直接创建 DeviceListScreen(),但没有传入任何点击回调,因此三个设备按钮都会使用空实现。任何未显式覆盖 devicesSlot 的调用方都会展示可点击但无响应的入口;应在路由完成前保留占位内容,或由壳层传入实际回调。

**无效入口** `MainAppShell` 默认直接创建 `DeviceListScreen()`,但没有传入任何点击回调,因此三个设备按钮都会使用空实现。任何未显式覆盖 `devicesSlot` 的调用方都会展示可点击但无响应的入口;应在路由完成前保留占位内容,或由壳层传入实际回调。 <!-- pr-agent-dedup: 5c1093080900 --> <!-- pr-agent-key-issue-location: 256791003a90 -->
activitySlot: @Composable () -> Unit = { DefaultActivitySlot() }, 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 @Composable
private fun DefaultScenesSlot() { private fun DefaultScenesSlot() {
SlotPlaceholder( SlotPlaceholder(
@@ -42,4 +42,14 @@
<string name="scenes_placeholder_description">情景动作与执行插槽(由 tzh 模块实现)。</string> <string name="scenes_placeholder_description">情景动作与执行插槽(由 tzh 模块实现)。</string>
<string name="activity_placeholder_title">活动记录</string> <string name="activity_placeholder_title">活动记录</string>
<string name="activity_placeholder_description">未记录任何事件。</string> <string name="activity_placeholder_description">未记录任何事件。</string>
<!-- 设备列表 -->
<string name="device_list_title">设备列表</string>
<string name="device_list_description">选择需要控制或查看状态的 ABox 终端设备。</string>
<string name="device_socket_title">智能插座</string>
<string name="device_socket_description">进入智能插座相关功能。</string>
<string name="device_temperature_humidity_title">温湿度传感器</string>
<string name="device_temperature_humidity_description">进入温湿度状态相关功能。</string>
<string name="device_infrared_title">红外控制器</string>
<string name="device_infrared_description">进入红外控制器相关功能。</string>
</resources> </resources>
@@ -42,4 +42,14 @@
<string name="scenes_placeholder_description">Scene actions and execution slot for tzh.</string> <string name="scenes_placeholder_description">Scene actions and execution slot for tzh.</string>
<string name="activity_placeholder_title">Activity Log</string> <string name="activity_placeholder_title">Activity Log</string>
<string name="activity_placeholder_description">No recorded events yet.</string> <string name="activity_placeholder_description">No recorded events yet.</string>
<!-- Device list -->
<string name="device_list_title">Device List</string>
<string name="device_list_description">Select an ABox terminal device to control or check status.</string>
<string name="device_socket_title">Smart Socket</string>
<string name="device_socket_description">Access smart socket features.</string>
<string name="device_temperature_humidity_title">Temperature &amp; Humidity Sensor</string>
<string name="device_temperature_humidity_description">View temperature and humidity status.</string>
<string name="device_infrared_title">Infrared Controller</string>
<string name="device_infrared_description">Access infrared controller features.</string>
</resources> </resources>