From 171d1daa509d6160d34dfc82687c916d62c71af7 Mon Sep 17 00:00:00 2001 From: KremeCN <3265494849@qq.com> Date: Wed, 26 Aug 2026 19:39:07 +0800 Subject: [PATCH] feat: unify device detail presentation --- .../abox/manager/ui/socket/SocketScreen.kt | 101 +++++------------- .../TemperatureHumidityScreen.kt | 97 +++++------------ .../manager/infrared/hub/InfraredHubScreen.kt | 18 ++-- .../infrared/panel/RemotePanelScreen.kt | 11 +- 4 files changed, 68 insertions(+), 159 deletions(-) diff --git a/android/app/src/main/kotlin/com/flagship/abox/manager/ui/socket/SocketScreen.kt b/android/app/src/main/kotlin/com/flagship/abox/manager/ui/socket/SocketScreen.kt index 62db0e1..8bd9dc5 100644 --- a/android/app/src/main/kotlin/com/flagship/abox/manager/ui/socket/SocketScreen.kt +++ b/android/app/src/main/kotlin/com/flagship/abox/manager/ui/socket/SocketScreen.kt @@ -47,7 +47,11 @@ import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.flagship.abox.manager.R import com.flagship.abox.manager.designsystem.ABoxButton +import com.flagship.abox.manager.designsystem.ABoxDeviceDetailScaffold +import com.flagship.abox.manager.designsystem.ABoxIconButton import com.flagship.abox.manager.designsystem.ABoxLoadingIndicator +import com.flagship.abox.manager.designsystem.icon.ABoxIcon +import com.flagship.abox.manager.designsystem.icon.ABoxIconKey import com.flagship.abox.manager.domain.DeviceName import com.flagship.abox.manager.domain.SocketPowerState import java.text.SimpleDateFormat @@ -79,39 +83,10 @@ fun SocketScreen( modifier: Modifier = Modifier, ) { val scrollState = rememberScrollState() - var showEditNameDialog by remember { mutableStateOf(false) } - - Scaffold( + ABoxDeviceDetailScaffold( modifier = modifier.fillMaxSize(), - topBar = { - TopAppBar( - title = { - Text( - text = state.deviceName?.value ?: stringResource(R.string.socket_title), - style = MaterialTheme.typography.titleLarge, - ) - }, - navigationIcon = { - IconButton(onClick = onBackClick) { - Text("<", style = MaterialTheme.typography.titleLarge, fontWeight = FontWeight.Bold) - } - }, - actions = { - IconButton( - onClick = { showEditNameDialog = true }, - enabled = !state.isLoading && !state.isOperating, - ) { - Text("✎", style = MaterialTheme.typography.titleMedium, fontWeight = FontWeight.Bold) - } - IconButton( - onClick = { onEvent(SocketUiEvent.Refresh) }, - enabled = !state.isLoading && !state.isOperating, - ) { - Text("↻", style = MaterialTheme.typography.titleMedium, fontWeight = FontWeight.Bold) - } - }, - ) - }, + title = state.deviceName?.value ?: stringResource(R.string.socket_title), + onBack = onBackClick, ) { paddingValues -> Column( modifier = Modifier @@ -121,6 +96,17 @@ fun SocketScreen( .verticalScroll(scrollState), horizontalAlignment = Alignment.CenterHorizontally, ) { + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.End, + ) { + ABoxIconButton( + key = ABoxIconKey.REFRESH, + contentDescription = "刷新插座状态", + onClick = { onEvent(SocketUiEvent.Refresh) }, + enabled = !state.isLoading && !state.isOperating, + ) + } if (state.isOffline) { OfflineBanner( onRetry = { onEvent(SocketUiEvent.Refresh) }, @@ -248,41 +234,6 @@ fun SocketScreen( } } - if (showEditNameDialog) { - var inputName by remember(state.deviceName) { mutableStateOf(state.deviceName?.value.orEmpty()) } - AlertDialog( - onDismissRequest = { showEditNameDialog = false }, - title = { Text(stringResource(R.string.dialog_edit_device_name_title)) }, - text = { - OutlinedTextField( - value = inputName, - onValueChange = { inputName = it }, - label = { Text(stringResource(R.string.dialog_edit_device_name_hint)) }, - singleLine = true, - modifier = Modifier.fillMaxWidth(), - ) - }, - confirmButton = { - TextButton( - onClick = { - val trimmed = inputName.trim() - if (trimmed.isNotBlank()) { - onEvent(SocketUiEvent.ChangeDeviceName(DeviceName(trimmed))) - } - showEditNameDialog = false - }, - enabled = inputName.isNotBlank(), - ) { - Text(stringResource(R.string.action_confirm)) - } - }, - dismissButton = { - TextButton(onClick = { showEditNameDialog = false }) { - Text(stringResource(R.string.action_cancel)) - } - }, - ) - } } @Composable @@ -310,10 +261,10 @@ private fun PowerIndicatorBadge( if (isLoading) { ABoxLoadingIndicator(modifier = Modifier.size(36.dp)) } else { - Text( - text = if (powerState == SocketPowerState.ON) "⚡" else "○", - style = MaterialTheme.typography.headlineLarge, - color = indicatorColor, + ABoxIcon( + key = if (powerState == SocketPowerState.ON) ABoxIconKey.POWER else ABoxIconKey.SOCKET, + contentDescription = if (powerState == SocketPowerState.ON) "插座已开启" else "插座已关闭", + modifier = Modifier.size(40.dp), ) } } @@ -377,9 +328,11 @@ private fun ErrorBanner( style = MaterialTheme.typography.bodySmall, modifier = Modifier.weight(1f), ) - TextButton(onClick = onDismiss) { - Text("✕") - } + ABoxIconButton( + key = ABoxIconKey.CLOSE, + contentDescription = "关闭错误提示", + onClick = onDismiss, + ) } } } diff --git a/android/app/src/main/kotlin/com/flagship/abox/manager/ui/temperaturehumidity/TemperatureHumidityScreen.kt b/android/app/src/main/kotlin/com/flagship/abox/manager/ui/temperaturehumidity/TemperatureHumidityScreen.kt index 1c2f13b..67796fe 100644 --- a/android/app/src/main/kotlin/com/flagship/abox/manager/ui/temperaturehumidity/TemperatureHumidityScreen.kt +++ b/android/app/src/main/kotlin/com/flagship/abox/manager/ui/temperaturehumidity/TemperatureHumidityScreen.kt @@ -39,7 +39,9 @@ import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.flagship.abox.manager.R import com.flagship.abox.manager.designsystem.ABoxButton +import com.flagship.abox.manager.designsystem.ABoxDeviceDetailScaffold import com.flagship.abox.manager.designsystem.ABoxLoadingIndicator +import com.flagship.abox.manager.designsystem.ABoxIconButton import com.flagship.abox.manager.designsystem.icon.ABoxIcon import com.flagship.abox.manager.designsystem.icon.ABoxIconKey import com.flagship.abox.manager.domain.DeviceName @@ -72,39 +74,10 @@ fun TemperatureHumidityScreen( modifier: Modifier = Modifier, ) { val scrollState = rememberScrollState() - var showEditNameDialog by remember { mutableStateOf(false) } - - Scaffold( + ABoxDeviceDetailScaffold( modifier = modifier.fillMaxSize(), - topBar = { - TopAppBar( - title = { - Text( - text = state.deviceName?.value ?: stringResource(R.string.th_title), - style = MaterialTheme.typography.titleLarge, - ) - }, - navigationIcon = { - IconButton(onClick = onBackClick) { - Text("<", style = MaterialTheme.typography.titleLarge, fontWeight = FontWeight.Bold) - } - }, - actions = { - IconButton( - onClick = { showEditNameDialog = true }, - enabled = !state.isLoading && !state.isRefreshing, - ) { - Text("✎", style = MaterialTheme.typography.titleMedium, fontWeight = FontWeight.Bold) - } - IconButton( - onClick = { onEvent(TemperatureHumidityUiEvent.Refresh) }, - enabled = !state.isLoading && !state.isRefreshing, - ) { - Text("↻", style = MaterialTheme.typography.titleMedium, fontWeight = FontWeight.Bold) - } - }, - ) - }, + title = state.deviceName?.value ?: stringResource(R.string.th_title), + onBack = onBackClick, ) { paddingValues -> Column( modifier = Modifier @@ -114,6 +87,17 @@ fun TemperatureHumidityScreen( .verticalScroll(scrollState), horizontalAlignment = Alignment.CenterHorizontally, ) { + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.End, + ) { + ABoxIconButton( + key = ABoxIconKey.REFRESH, + contentDescription = "刷新温湿度数据", + onClick = { onEvent(TemperatureHumidityUiEvent.Refresh) }, + enabled = !state.isLoading && !state.isRefreshing, + ) + } if (state.isOffline) { OfflineBanner( onRetry = { onEvent(TemperatureHumidityUiEvent.Refresh) }, @@ -241,41 +225,6 @@ fun TemperatureHumidityScreen( } } - if (showEditNameDialog) { - var inputName by remember(state.deviceName) { mutableStateOf(state.deviceName?.value.orEmpty()) } - AlertDialog( - onDismissRequest = { showEditNameDialog = false }, - title = { Text(stringResource(R.string.dialog_edit_device_name_title)) }, - text = { - OutlinedTextField( - value = inputName, - onValueChange = { inputName = it }, - label = { Text(stringResource(R.string.dialog_edit_device_name_hint)) }, - singleLine = true, - modifier = Modifier.fillMaxWidth(), - ) - }, - confirmButton = { - TextButton( - onClick = { - val trimmed = inputName.trim() - if (trimmed.isNotBlank()) { - onEvent(TemperatureHumidityUiEvent.ChangeDeviceName(DeviceName(trimmed))) - } - showEditNameDialog = false - }, - enabled = inputName.isNotBlank(), - ) { - Text(stringResource(R.string.action_confirm)) - } - }, - dismissButton = { - TextButton(onClick = { showEditNameDialog = false }) { - Text(stringResource(R.string.action_cancel)) - } - }, - ) - } } @Composable @@ -353,7 +302,11 @@ private fun NoDataCard( horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center, ) { - Text("📭", style = MaterialTheme.typography.displaySmall) + ABoxIcon( + key = ABoxIconKey.SENSOR, + contentDescription = "暂无传感器数据", + modifier = Modifier.size(40.dp), + ) Spacer(modifier = Modifier.height(12.dp)) Text( text = stringResource(R.string.th_no_data_title), @@ -432,9 +385,11 @@ private fun ErrorBanner( style = MaterialTheme.typography.bodySmall, modifier = Modifier.weight(1f), ) - TextButton(onClick = onDismiss) { - Text("✕") - } + ABoxIconButton( + key = ABoxIconKey.CLOSE, + contentDescription = "关闭错误提示", + onClick = onDismiss, + ) } } } diff --git a/android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/hub/InfraredHubScreen.kt b/android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/hub/InfraredHubScreen.kt index 1ee37cb..cccbbf1 100644 --- a/android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/hub/InfraredHubScreen.kt +++ b/android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/hub/InfraredHubScreen.kt @@ -59,6 +59,8 @@ import androidx.compose.ui.unit.sp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.flagship.abox.manager.designsystem.ABoxSurface +import com.flagship.abox.manager.designsystem.ABoxIconButton +import com.flagship.abox.manager.designsystem.icon.ABoxIconKey import com.flagship.abox.manager.domain.ProviderProfileId import com.flagship.abox.manager.domain.infrared.PanelLayoutStyle import com.flagship.abox.manager.domain.infrared.PresetCodeLibrary @@ -154,17 +156,15 @@ internal fun InfraredHubScreen( } }, navigationIcon = { - TextButton(onClick = onBack) { - Text(stringResource(R.string.infrared_back)) - } + ABoxIconButton(ABoxIconKey.BACK, "返回", onBack) }, actions = { - IconButton( + ABoxIconButton( + key = ABoxIconKey.EDIT, + contentDescription = "打开红外设置", onClick = onNavigateToSettings, modifier = Modifier.testTag("hub_settings_button"), - ) { - Text("⚙️", fontSize = 20.sp) - } + ) }, ) }, @@ -199,7 +199,7 @@ internal fun InfraredHubScreen( onClick = { showCreateDialog = true }, modifier = Modifier.testTag("hub_add_remote_button"), ) { - Text("➕ " + stringResource(R.string.infrared_hub_add_remote)) + Text(stringResource(R.string.infrared_hub_add_remote)) } } @@ -278,7 +278,7 @@ private fun VirtualRemoteCard( } Text(icon, fontSize = 28.sp) IconButton(onClick = onDelete, modifier = Modifier.testTag("delete_remote_${remote.id.value}")) { - Text("🗑️", fontSize = 16.sp) + Text(stringResource(R.string.infrared_delete)) } } diff --git a/android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/panel/RemotePanelScreen.kt b/android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/panel/RemotePanelScreen.kt index 34f4dd6..cc0a73a 100644 --- a/android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/panel/RemotePanelScreen.kt +++ b/android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/panel/RemotePanelScreen.kt @@ -60,6 +60,9 @@ import androidx.compose.ui.unit.sp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.flagship.abox.manager.designsystem.ABoxSurface +import com.flagship.abox.manager.designsystem.ABoxIconButton +import com.flagship.abox.manager.designsystem.icon.ABoxIcon +import com.flagship.abox.manager.designsystem.icon.ABoxIconKey import com.flagship.abox.manager.domain.infrared.AcFanSpeed import com.flagship.abox.manager.domain.infrared.AcMode import com.flagship.abox.manager.domain.infrared.AcStateSnapshot @@ -175,9 +178,7 @@ internal fun RemotePanelScreen( } }, navigationIcon = { - TextButton(onClick = onBack) { - Text(stringResource(R.string.infrared_back)) - } + ABoxIconButton(ABoxIconKey.BACK, "返回", onBack) }, actions = { // 非空调面板显示学习模式开关 @@ -185,7 +186,7 @@ internal fun RemotePanelScreen( FilterChip( selected = state.isLearnMode, onClick = onToggleLearnMode, - label = { Text(if (state.isLearnMode) "🔴 录制中" else "⚪ 学习模式") }, + label = { Text(if (state.isLearnMode) "录制中" else "学习模式") }, colors = FilterChipDefaults.filterChipColors( selectedContainerColor = Color(0xFFFF5252), selectedLabelColor = Color.White, @@ -222,7 +223,7 @@ internal fun RemotePanelScreen( verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(8.dp), ) { - Text("🔴", fontSize = 16.sp) + ABoxIcon(ABoxIconKey.PENDING, "录制中", modifier = Modifier.size(20.dp)) Text( text = "学习模式已开启:点击任意按键,然后将物理实体遥控器对准转发器按下即可录码", color = Color(0xFFC62828),