diff --git a/android/app/src/main/kotlin/com/flagship/abox/manager/ui/scene/SceneEditorScreen.kt b/android/app/src/main/kotlin/com/flagship/abox/manager/ui/scene/SceneEditorScreen.kt index 5c6a765..22f275f 100644 --- a/android/app/src/main/kotlin/com/flagship/abox/manager/ui/scene/SceneEditorScreen.kt +++ b/android/app/src/main/kotlin/com/flagship/abox/manager/ui/scene/SceneEditorScreen.kt @@ -747,7 +747,7 @@ private fun ActionItemRow( @Composable private fun SceneTrigger.describe(): String = when (this) { - is SceneTrigger.Time -> "⏰ $timeOfDay" + is SceneTrigger.Time -> "定时 $timeOfDay" is SceneTrigger.Temperature -> stringResource(R.string.scene_trigger_desc_temp, operator.symbol, thresholdCelsius.toInt().toString()) is SceneTrigger.Humidity -> stringResource(R.string.scene_trigger_desc_humidity, operator.symbol, thresholdPercent.toInt().toString()) is SceneTrigger.DeviceState -> stringResource(R.string.scene_trigger_desc_device, deviceName.value, expectedState) diff --git a/android/app/src/main/kotlin/com/flagship/abox/manager/ui/scene/SceneListScreen.kt b/android/app/src/main/kotlin/com/flagship/abox/manager/ui/scene/SceneListScreen.kt index 5560346..a2659fd 100644 --- a/android/app/src/main/kotlin/com/flagship/abox/manager/ui/scene/SceneListScreen.kt +++ b/android/app/src/main/kotlin/com/flagship/abox/manager/ui/scene/SceneListScreen.kt @@ -68,6 +68,8 @@ import com.flagship.abox.manager.domain.Scene import com.flagship.abox.manager.domain.SceneColor import com.flagship.abox.manager.domain.SceneIcon import com.flagship.abox.manager.domain.SceneId +import com.flagship.abox.manager.designsystem.icon.ABoxIcon +import com.flagship.abox.manager.designsystem.icon.ABoxIconKey import kotlinx.coroutines.flow.collectLatest @OptIn(ExperimentalMaterial3Api::class) @@ -250,9 +252,10 @@ private fun SceneCard( ), contentAlignment = Alignment.Center, ) { - Text( - text = scene.icon.displayEmoji(), - style = MaterialTheme.typography.titleMedium, + ABoxIcon( + key = scene.icon.toABoxIconKey(), + contentDescription = scene.name, + modifier = Modifier.size(28.dp), ) } Spacer(Modifier.width(12.dp)) @@ -375,10 +378,7 @@ private fun EmptySceneList(modifier: Modifier = Modifier) { horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center, ) { - Text( - text = "⚡", - style = MaterialTheme.typography.displayMedium, - ) + ABoxIcon(ABoxIconKey.SCENES, contentDescription = "暂无情景", modifier = Modifier.size(48.dp)) Spacer(Modifier.size(8.dp)) Text( text = stringResource(R.string.scene_empty_title), @@ -396,19 +396,19 @@ private fun EmptySceneList(modifier: Modifier = Modifier) { } } -fun SceneIcon.displayEmoji(): String = when (this) { - SceneIcon.HOME -> "🏠" - SceneIcon.OFFICE -> "💼" - SceneIcon.NIGHT -> "🌙" - SceneIcon.MOVIE -> "🎬" - SceneIcon.AWAY -> "🚶" - SceneIcon.READING -> "📖" - SceneIcon.DINING -> "🍽️" - SceneIcon.GAMING -> "🎮" - SceneIcon.THERMOSTAT -> "🌡️" - SceneIcon.LIGHT -> "💡" - SceneIcon.CLEAN -> "🧹" - SceneIcon.CUSTOM -> "✨" +fun SceneIcon.toABoxIconKey(): ABoxIconKey = when (this) { + SceneIcon.HOME -> ABoxIconKey.DEVICES + SceneIcon.OFFICE -> ABoxIconKey.ACTIVITY + SceneIcon.NIGHT -> ABoxIconKey.PENDING + SceneIcon.MOVIE -> ABoxIconKey.SCENES + SceneIcon.AWAY -> ABoxIconKey.OFFLINE + SceneIcon.READING -> ABoxIconKey.DEVICES + SceneIcon.DINING -> ABoxIconKey.DEVICES + SceneIcon.GAMING -> ABoxIconKey.SCENES + SceneIcon.THERMOSTAT -> ABoxIconKey.TEMPERATURE + SceneIcon.LIGHT -> ABoxIconKey.POWER + SceneIcon.CLEAN -> ABoxIconKey.REFRESH + SceneIcon.CUSTOM -> ABoxIconKey.UNKNOWN_DEVICE } internal fun SceneColor.toColorOrNull(): Color? { 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 89d8729..6edd76b 100644 --- a/android/app/src/main/res/values-zh-rCN/strings.xml +++ b/android/app/src/main/res/values-zh-rCN/strings.xml @@ -82,9 +82,9 @@ 其他选项与外观 - + 新增触发器 - + 新增条件 - + 新增动作 + 新增触发器 + 新增条件 + 新增动作 添加 保存指令 @@ -104,10 +104,10 @@ 选择触发器类型 - ⏰ 定时时间触发 - 🌡️ 环境温度触发 - 💧 环境湿度触发 - 🔌 插座状态变更触发 + 定时时间触发 + 环境温度触发 + 环境湿度触发 + 插座状态变更触发 触发时间点(如:07:30) 温度阈值 (°C) 湿度阈值 (%) @@ -118,19 +118,19 @@ 选择限制条件 - 🌡️ 环境温度范围 - 💧 环境湿度范围 - 🔌 插座当前电源状态 - 🕒 生效时间区间 + 环境温度范围 + 环境湿度范围 + 插座当前电源状态 + 生效时间区间 起始时间(如:22:00) 结束时间(如:07:00) 选择执行动作 - 🔌 智能插座控制 - 📺 红外遥控按键 - ⏱️ 延时等待 (缓冲) - 🔔 发送系统提示 + 智能插座控制 + 红外遥控按键 + 延时等待(缓冲) + 发送系统提示 选择目标插座 设定电源状态 选择虚拟遥控器 @@ -144,8 +144,8 @@ 插座【%1$s】: 设为 %2$s 遥控【%1$s】: 发送 %2$s 红外【%1$s】: 槽位 %2$s - ⏱️ 等待 %1$d 秒 - 🔔 提示: %1$s + 等待 %1$d 秒 + 提示:%1$s 执行指令 @@ -164,13 +164,13 @@ 时间格式无效(请按 HH:mm 格式输入,如 07:30) 时间格式无效(请按 HH:mm 格式输入,如 22:00) - ⏰ 定时 - 🌡️ 温度 - 💧 湿度 - 🔌 插座 - 📺 遥控 - ⏱️ 延时 - 🔔 提示 + 定时 + 温度 + 湿度 + 插座 + 遥控 + 延时 + 提示 > 高于 < 低于 开启 @@ -184,7 +184,7 @@ 尚未添加触发器,请点击下方按钮添加 尚未添加限制条件,请点击下方按钮添加 尚未添加执行动作,请点击下方按钮添加 - ⚡ %1$d 个触发器 • %2$d 个动作 + %1$d 个触发器 · %2$d 个动作 %1$d 个执行动作 点击下方按钮立即开始执行 重新执行 diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml index e3be99b..20b7022 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml @@ -82,9 +82,9 @@ Other Options & Appearance - + Add Trigger - + Add Condition - + Add Action + Add Trigger + Add Condition + Add Action Add Save Command @@ -104,10 +104,10 @@ Select Trigger Type - ⏰ Time / Schedule Trigger - 🌡️ Temperature Threshold - 💧 Humidity Threshold - 🔌 Socket State Changed + Time / Schedule Trigger + Temperature Threshold + Humidity Threshold + Socket State Changed Trigger Time (e.g. 07:30) Temperature Threshold (°C) Humidity Threshold (%) @@ -118,19 +118,19 @@ Select Condition - 🌡️ Temperature Range - 💧 Humidity Range - 🔌 Current Socket Power - 🕒 Active Time Range + Temperature Range + Humidity Range + Current Socket Power + Active Time Range Start Time (e.g. 22:00) End Time (e.g. 07:00) Select Action - 🔌 Smart Socket Control - 📺 Infrared Remote Button - ⏱️ Delay / Buffer Timer - 🔔 System Notification + Smart Socket Control + Infrared Remote Button + Delay / Buffer Timer + System Notification Select Target Socket Target Power State Select Virtual Remote @@ -144,8 +144,8 @@ Socket [%1$s]: Set %2$s Remote [%1$s]: Send %2$s Infrared [%1$s]: Slot %2$s - ⏱️ Delay %1$d seconds - 🔔 Notify: %1$s + Delay %1$d seconds + Notify: %1$s Execute Command @@ -164,13 +164,13 @@ Invalid time format (use HH:mm, e.g. 07:30) Invalid time format (use HH:mm, e.g. 22:00) - ⏰ Schedule - 🌡️ Temperature - 💧 Humidity - 🔌 Socket - 📺 Remote - ⏱️ Delay - 🔔 Notify + Schedule + Temperature + Humidity + Socket + Remote + Delay + Notify > Above < Below Turn ON @@ -184,7 +184,7 @@ No triggers configured. Tap + below to add. No conditions configured. Tap + below to add. No actions configured. Tap + below to add. - ⚡ %1$d triggers • %2$d actions + %1$d triggers · %2$d actions %1$d actions Tap the button below to run immediately Re-run diff --git a/android/feature/infrared/build.gradle.kts b/android/feature/infrared/build.gradle.kts index 2bf2ebb..38c0709 100644 --- a/android/feature/infrared/build.gradle.kts +++ b/android/feature/infrared/build.gradle.kts @@ -57,6 +57,7 @@ dependencies { implementation(libs.compose.ui) implementation(libs.compose.ui.tooling.preview) implementation(libs.compose.material3) + implementation(libs.compose.material.icons.extended) testImplementation(libs.junit) testImplementation(libs.kotlinx.coroutines.test) 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 cccbbf1..b8bb0a7 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 @@ -271,10 +271,10 @@ private fun VirtualRemoteCard( verticalAlignment = Alignment.CenterVertically, ) { val icon = when (remote.type) { - RemoteDeviceType.TV -> "📺" - RemoteDeviceType.AIR_CONDITIONER -> "❄️" + RemoteDeviceType.TV -> "电视" + RemoteDeviceType.AIR_CONDITIONER -> "空调" RemoteDeviceType.FAN -> "🌀" - RemoteDeviceType.CUSTOM -> "🎛️" + RemoteDeviceType.CUSTOM -> "自定义" } Text(icon, fontSize = 28.sp) IconButton(onClick = onDelete, modifier = Modifier.testTag("delete_remote_${remote.id.value}")) { @@ -288,11 +288,11 @@ private fun VirtualRemoteCard( ) val modeText = if (remote.type == RemoteDeviceType.AIR_CONDITIONER) { - "❄️ 状态机动态写码" + "状态机动态写码" } else if (remote.transmissionMode == TransmissionMode.STATIC_SLOTS) { - "🚀 静态独占槽位" + "静态独占槽位" } else { - "📦 动态写码" + "动态写码" } Text( @@ -430,7 +430,7 @@ private fun CreateVirtualRemoteDialog( OutlinedTextField( value = name, onValueChange = { name = it }, - label = { Text("遥控器名称(如:客厅电视、我的遥控器)") }, + label = { Text("遥控器名称(如:电视、我的遥控器)") }, singleLine = true, modifier = Modifier.fillMaxWidth().testTag("create_remote_name_input"), ) @@ -443,10 +443,10 @@ private fun CreateVirtualRemoteDialog( ) { OutlinedTextField( value = when (selectedCustomLayout) { - PanelLayoutStyle.TV_NAV -> "📺 电视导航布局 (内置方向盘/音量/频道)" - PanelLayoutStyle.MEDIA_PLAYER -> "🎵 媒体播放器布局 (播放/快进/切歌)" - PanelLayoutStyle.GRID -> "🎛️ 自由网格布局 (可自由增删按键)" - PanelLayoutStyle.AC_COMPACT -> "❄️ 空调布局" + PanelLayoutStyle.TV_NAV -> "电视导航布局(内置方向盘、音量、频道)" + PanelLayoutStyle.MEDIA_PLAYER -> "媒体播放器布局(播放、快进、切歌)" + PanelLayoutStyle.GRID -> "自由网格布局(可自由增删按键)" + PanelLayoutStyle.AC_COMPACT -> "空调布局" }, onValueChange = {}, readOnly = true, @@ -459,21 +459,21 @@ private fun CreateVirtualRemoteDialog( onDismissRequest = { layoutDropdownExpanded = false }, ) { DropdownMenuItem( - text = { Text("📺 电视导航布局 (内置方向盘/音量/频道)") }, + text = { Text("电视导航布局(内置方向盘、音量、频道)") }, onClick = { selectedCustomLayout = PanelLayoutStyle.TV_NAV layoutDropdownExpanded = false }, ) DropdownMenuItem( - text = { Text("🎵 媒体播放器布局 (播放/快进/切歌)") }, + text = { Text("媒体播放器布局(播放、快进、切歌)") }, onClick = { selectedCustomLayout = PanelLayoutStyle.MEDIA_PLAYER layoutDropdownExpanded = false }, ) DropdownMenuItem( - text = { Text("🎛️ 自由网格布局 (可自由增删按键)") }, + text = { Text("自由网格布局(可自由增删按键)") }, onClick = { selectedCustomLayout = PanelLayoutStyle.GRID layoutDropdownExpanded = false @@ -538,7 +538,7 @@ private fun CreateVirtualRemoteDialog( onClick = { transmissionMode = TransmissionMode.STATIC_SLOTS }, ) Column { - Text("🚀 静态独占槽位模式", fontWeight = FontWeight.Medium) + Text("静态独占槽位模式", fontWeight = FontWeight.Medium) Text(stringResource(R.string.infrared_hub_trans_mode_static_desc), fontSize = 11.sp, color = MaterialTheme.colorScheme.onSurfaceVariant) } } @@ -552,7 +552,7 @@ private fun CreateVirtualRemoteDialog( onClick = { transmissionMode = TransmissionMode.DYNAMIC_SCRATCH }, ) Column { - Text("📦 动态写码模式", fontWeight = FontWeight.Medium) + Text("动态写码模式", fontWeight = FontWeight.Medium) Text(stringResource(R.string.infrared_hub_trans_mode_dynamic_desc), fontSize = 11.sp, color = MaterialTheme.colorScheme.onSurfaceVariant) } } @@ -563,7 +563,7 @@ private fun CreateVirtualRemoteDialog( modifier = Modifier.fillMaxWidth(), ) { Text( - text = "❄️ 空调多维状态自动采用 App 状态机 + 动态缓冲写码,杜绝状态踩踏并节省槽位。", + text = "空调多维状态自动采用状态机和动态缓冲写码,避免状态冲突并节省槽位。", fontSize = 12.sp, modifier = Modifier.padding(10.dp), color = MaterialTheme.colorScheme.onSecondaryContainer, 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 cc0a73a..654a178 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 @@ -41,6 +41,16 @@ import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.material3.TopAppBar +import androidx.compose.material.icons.outlined.Add +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.KeyboardArrowDown +import androidx.compose.material.icons.outlined.KeyboardArrowLeft +import androidx.compose.material.icons.outlined.KeyboardArrowRight +import androidx.compose.material.icons.outlined.KeyboardArrowUp +import androidx.compose.material.icons.outlined.PowerSettingsNew +import androidx.compose.material.icons.outlined.Remove +import androidx.compose.material.icons.outlined.VolumeOff +import androidx.compose.material.icons.outlined.Tv import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue @@ -333,7 +343,7 @@ private fun AcControlPanel( verticalAlignment = Alignment.CenterVertically, ) { Text( - text = if (acState.power) "● 运行中" else "○ 已关机", + text = if (acState.power) "运行中" else "已关机", color = if (acState.power) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.onSurfaceVariant, fontWeight = FontWeight.Bold, ) @@ -345,7 +355,7 @@ private fun AcControlPanel( .background(if (acState.power) Color(0xFFFF5252) else MaterialTheme.colorScheme.outline) .testTag("ac_power_toggle"), ) { - Text("⏻", color = Color.White, fontSize = 20.sp, fontWeight = FontWeight.Bold) + Icon(Icons.Outlined.PowerSettingsNew, contentDescription = "电源", tint = Color.White) } } @@ -371,14 +381,14 @@ private fun AcControlPanel( modifier = Modifier.size(56.dp).testTag("ac_temp_down"), shape = CircleShape, ) { - Text("-", fontSize = 24.sp, fontWeight = FontWeight.Bold) + Icon(Icons.Outlined.Remove, contentDescription = "降低温度") } Button( onClick = { onAdjustTemp(1) }, modifier = Modifier.size(56.dp).testTag("ac_temp_up"), shape = CircleShape, ) { - Text("+", fontSize = 24.sp, fontWeight = FontWeight.Bold) + Icon(Icons.Outlined.Add, contentDescription = "升高温度") } } } @@ -421,19 +431,19 @@ private fun AcControlPanel( FilterChip( selected = acState.verticalSwing, onClick = onToggleVSwing, - label = { Text("↕ 上下扫风") }, + label = { Text("上下扫风") }, modifier = Modifier.weight(1f), ) FilterChip( selected = acState.horizontalSwing, onClick = onToggleHSwing, - label = { Text("↔ 左右扫风") }, + label = { Text("左右扫风") }, modifier = Modifier.weight(1f), ) FilterChip( selected = acState.eco, onClick = onToggleEco, - label = { Text("🌱 节能 ECO") }, + label = { Text("节能") }, modifier = Modifier.weight(1f), ) } @@ -463,9 +473,9 @@ private fun TvControlPanel( horizontalArrangement = Arrangement.SpaceAround, verticalAlignment = Alignment.CenterVertically, ) { - InteractiveRemoteButton("TV_POWER", "⏻ 电源", Color(0xFFFF5252), bindingMap["TV_POWER"], isLearnMode, learningButtonId, sendingButtonId, onButtonClick) - InteractiveRemoteButton("TV_MUTE", "🔇 静音", MaterialTheme.colorScheme.secondary, bindingMap["TV_MUTE"], isLearnMode, learningButtonId, sendingButtonId, onButtonClick) - InteractiveRemoteButton("TV_SOURCE", "📺 信号源", MaterialTheme.colorScheme.secondary, bindingMap["TV_SOURCE"], isLearnMode, learningButtonId, sendingButtonId, onButtonClick) + InteractiveRemoteButton("TV_POWER", "电源", Color(0xFFFF5252), bindingMap["TV_POWER"], isLearnMode, learningButtonId, sendingButtonId, onButtonClick) + InteractiveRemoteButton("TV_MUTE", "静音", MaterialTheme.colorScheme.secondary, bindingMap["TV_MUTE"], isLearnMode, learningButtonId, sendingButtonId, onButtonClick) + InteractiveRemoteButton("TV_SOURCE", "信号源", MaterialTheme.colorScheme.secondary, bindingMap["TV_SOURCE"], isLearnMode, learningButtonId, sendingButtonId, onButtonClick) } Spacer(modifier = Modifier.height(8.dp)) @@ -482,7 +492,7 @@ private fun TvControlPanel( onClick = { onButtonClick("TV_NAV_UP") }, modifier = Modifier.align(Alignment.TopCenter).padding(top = 10.dp).testTag("tv_nav_up"), ) { - ButtonIndicatorText("TV_NAV_UP", "▲", learningButtonId, sendingButtonId) + ButtonIndicatorText("TV_NAV_UP", "上", learningButtonId, sendingButtonId) } // 下 @@ -490,7 +500,7 @@ private fun TvControlPanel( onClick = { onButtonClick("TV_NAV_DOWN") }, modifier = Modifier.align(Alignment.BottomCenter).padding(bottom = 10.dp).testTag("tv_nav_down"), ) { - ButtonIndicatorText("TV_NAV_DOWN", "▼", learningButtonId, sendingButtonId) + ButtonIndicatorText("TV_NAV_DOWN", "下", learningButtonId, sendingButtonId) } // 左 @@ -498,7 +508,7 @@ private fun TvControlPanel( onClick = { onButtonClick("TV_NAV_LEFT") }, modifier = Modifier.align(Alignment.CenterStart).padding(start = 10.dp).testTag("tv_nav_left"), ) { - ButtonIndicatorText("TV_NAV_LEFT", "◀", learningButtonId, sendingButtonId) + ButtonIndicatorText("TV_NAV_LEFT", "左", learningButtonId, sendingButtonId) } // 右 @@ -506,7 +516,7 @@ private fun TvControlPanel( onClick = { onButtonClick("TV_NAV_RIGHT") }, modifier = Modifier.align(Alignment.CenterEnd).padding(end = 10.dp).testTag("tv_nav_right"), ) { - ButtonIndicatorText("TV_NAV_RIGHT", "▶", learningButtonId, sendingButtonId) + ButtonIndicatorText("TV_NAV_RIGHT", "右", learningButtonId, sendingButtonId) } // OK 居中大按钮 @@ -554,10 +564,10 @@ private fun TvControlPanel( ) { Text("频道 CH", fontWeight = FontWeight.Bold, fontSize = 12.sp) Button(onClick = { onButtonClick("TV_CH_UP") }, modifier = Modifier.size(54.dp), shape = RoundedCornerShape(12.dp)) { - ButtonIndicatorText("TV_CH_UP", "▲", learningButtonId, sendingButtonId) + ButtonIndicatorText("TV_CH_UP", "频道上调", learningButtonId, sendingButtonId) } Button(onClick = { onButtonClick("TV_CH_DOWN") }, modifier = Modifier.size(54.dp), shape = RoundedCornerShape(12.dp)) { - ButtonIndicatorText("TV_CH_DOWN", "▼", learningButtonId, sendingButtonId) + ButtonIndicatorText("TV_CH_DOWN", "频道下调", learningButtonId, sendingButtonId) } } } @@ -583,8 +593,8 @@ private fun MediaPlayerControlPanel( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween, ) { - InteractiveRemoteButton("MEDIA_POWER", "⏻ 电源", Color(0xFFFF5252), bindingMap["MEDIA_POWER"], isLearnMode, learningButtonId, sendingButtonId, onButtonClick) - InteractiveRemoteButton("MEDIA_MUTE", "🔇 静音", MaterialTheme.colorScheme.secondary, bindingMap["MEDIA_MUTE"], isLearnMode, learningButtonId, sendingButtonId, onButtonClick) + InteractiveRemoteButton("MEDIA_POWER", "电源", Color(0xFFFF5252), bindingMap["MEDIA_POWER"], isLearnMode, learningButtonId, sendingButtonId, onButtonClick) + InteractiveRemoteButton("MEDIA_MUTE", "静音", MaterialTheme.colorScheme.secondary, bindingMap["MEDIA_MUTE"], isLearnMode, learningButtonId, sendingButtonId, onButtonClick) } Spacer(modifier = Modifier.height(16.dp)) @@ -642,13 +652,13 @@ private fun GenericGridControlPanel( ) { Text("按键列表 (${bindings.size})", fontWeight = FontWeight.Bold, fontSize = 16.sp) Button(onClick = onAddButton, modifier = Modifier.testTag("grid_add_key_button")) { - Text("➕ 添加按键") + Text("添加按键") } } if (bindings.isEmpty()) { Box(modifier = Modifier.fillMaxWidth().weight(1f), contentAlignment = Alignment.Center) { - Text("暂无自定义按键,点击上方【➕ 添加按键】新建", color = MaterialTheme.colorScheme.onSurfaceVariant) + Text("暂无自定义按键,请使用上方添加操作新建", color = MaterialTheme.colorScheme.onSurfaceVariant) } } else { LazyVerticalGrid( @@ -697,9 +707,9 @@ private fun GridButtonCard( Column(modifier = Modifier.weight(1f)) { Text(binding.displayName, fontWeight = FontWeight.Bold, fontSize = 15.sp) val statusText = when { - isLearning -> "🟡 正在录制信号..." - binding.status == InfraredKeyStatus.READY -> "🟢 已就绪 (Slot ${binding.assignedSlot ?: "动态"})" - else -> "⚪ 待录制 (点击录码)" + isLearning -> "正在录制信号..." + binding.status == InfraredKeyStatus.READY -> "已就绪(槽位 ${binding.assignedSlot ?: "动态"})" + else -> "待录制(点击录码)" } Text(statusText, fontSize = 11.sp, color = MaterialTheme.colorScheme.onSurfaceVariant) } @@ -712,7 +722,7 @@ private fun GridButtonCard( onClick = onDelete, modifier = Modifier.size(36.dp).testTag("delete_key_${binding.buttonId}"), ) { - Text("🗑️", fontSize = 15.sp) + Text("删除", fontSize = 12.sp) } } } @@ -756,7 +766,7 @@ private fun ButtonIndicatorText( sendingButtonId: String?, ) { if (learningButtonId == buttonId) { - Text("🟡 录制中", fontSize = 12.sp, fontWeight = FontWeight.Bold) + Text("录制中", fontSize = 12.sp, fontWeight = FontWeight.Bold) } else if (sendingButtonId == buttonId) { CircularProgressIndicator(modifier = Modifier.size(16.dp), color = Color.White) } else { diff --git a/android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/remote/RemoteModels.kt b/android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/remote/RemoteModels.kt index bb50d7a..3d59edb 100644 --- a/android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/remote/RemoteModels.kt +++ b/android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/remote/RemoteModels.kt @@ -26,24 +26,24 @@ data class RemoteButtonSpec( object RemoteSceneConfig { object AirConditioner { - val POWER = RemoteButtonSpec("AC_POWER", "1", R.string.infrared_key_ac_power, "空调-电源", "⏻") - val TEMP_UP = RemoteButtonSpec("AC_TEMP_UP", "2", R.string.infrared_key_ac_temp_up, "空调-温度+", "▲") - val TEMP_DOWN = RemoteButtonSpec("AC_TEMP_DOWN", "3", R.string.infrared_key_ac_temp_down, "空调-温度-", "▼") - val MODE = RemoteButtonSpec("AC_MODE", "4", R.string.infrared_key_ac_mode, "空调-模式", "模式 ❄/☀") - val FAN = RemoteButtonSpec("AC_FAN", "5", R.string.infrared_key_ac_fan, "空调-风速", "风速 💨") - val SWING = RemoteButtonSpec("AC_SWING", "6", R.string.infrared_key_ac_swing, "空调-扫风", "扫风 ↔") - val TIMER = RemoteButtonSpec("AC_TIMER", "7", R.string.infrared_key_ac_timer, "空调-定时", "定时 ⏱") + val POWER = RemoteButtonSpec("AC_POWER", "1", R.string.infrared_key_ac_power, "空调-电源", "电源") + val TEMP_UP = RemoteButtonSpec("AC_TEMP_UP", "2", R.string.infrared_key_ac_temp_up, "空调-温度+", "升温") + val TEMP_DOWN = RemoteButtonSpec("AC_TEMP_DOWN", "3", R.string.infrared_key_ac_temp_down, "空调-温度-", "降温") + val MODE = RemoteButtonSpec("AC_MODE", "4", R.string.infrared_key_ac_mode, "空调-模式", "模式") + val FAN = RemoteButtonSpec("AC_FAN", "5", R.string.infrared_key_ac_fan, "空调-风速", "风速") + val SWING = RemoteButtonSpec("AC_SWING", "6", R.string.infrared_key_ac_swing, "空调-扫风", "扫风") + val TIMER = RemoteButtonSpec("AC_TIMER", "7", R.string.infrared_key_ac_timer, "空调-定时", "定时") val buttons = listOf(POWER, TEMP_UP, TEMP_DOWN, MODE, FAN, SWING, TIMER) } object Television { - val POWER = RemoteButtonSpec("TV_POWER", "21", R.string.infrared_key_tv_power, "电视-电源", "⏻") - val MUTE = RemoteButtonSpec("TV_MUTE", "22", R.string.infrared_key_tv_mute, "电视-静音", "🔇") - val NAV_UP = RemoteButtonSpec("TV_NAV_UP", "23", R.string.infrared_key_tv_nav_up, "电视-上", "▲") - val NAV_DOWN = RemoteButtonSpec("TV_NAV_DOWN", "24", R.string.infrared_key_tv_nav_down, "电视-下", "▼") - val NAV_LEFT = RemoteButtonSpec("TV_NAV_LEFT", "25", R.string.infrared_key_tv_nav_left, "电视-左", "◀") - val NAV_RIGHT = RemoteButtonSpec("TV_NAV_RIGHT", "26", R.string.infrared_key_tv_nav_right, "电视-右", "▶") + val POWER = RemoteButtonSpec("TV_POWER", "21", R.string.infrared_key_tv_power, "电视-电源", "电源") + val MUTE = RemoteButtonSpec("TV_MUTE", "22", R.string.infrared_key_tv_mute, "电视-静音", "静音") + val NAV_UP = RemoteButtonSpec("TV_NAV_UP", "23", R.string.infrared_key_tv_nav_up, "电视-上", "上") + val NAV_DOWN = RemoteButtonSpec("TV_NAV_DOWN", "24", R.string.infrared_key_tv_nav_down, "电视-下", "下") + val NAV_LEFT = RemoteButtonSpec("TV_NAV_LEFT", "25", R.string.infrared_key_tv_nav_left, "电视-左", "左") + val NAV_RIGHT = RemoteButtonSpec("TV_NAV_RIGHT", "26", R.string.infrared_key_tv_nav_right, "电视-右", "右") val OK = RemoteButtonSpec("TV_OK", "27", R.string.infrared_key_tv_ok, "电视-确定", "OK") val VOL_UP = RemoteButtonSpec("TV_VOL_UP", "28", R.string.infrared_key_tv_vol_up, "电视-音量+", "VOL +") val VOL_DOWN = RemoteButtonSpec("TV_VOL_DOWN", "29", R.string.infrared_key_tv_vol_down, "电视-音量-", "VOL -") diff --git a/android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/remote/ui/InfraredRemoteScreen.kt b/android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/remote/ui/InfraredRemoteScreen.kt index 97ccfc3..57b3678 100644 --- a/android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/remote/ui/InfraredRemoteScreen.kt +++ b/android/feature/infrared/src/main/kotlin/com/flagship/abox/manager/infrared/remote/ui/InfraredRemoteScreen.kt @@ -45,6 +45,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.infrared.R import com.flagship.abox.manager.infrared.remote.InfraredRemoteUiState import com.flagship.abox.manager.infrared.remote.InfraredRemoteViewModel @@ -158,12 +160,12 @@ internal fun InfraredRemoteScreen( } // 按键管理 / 设置入口按钮 - IconButton( + ABoxIconButton( + key = ABoxIconKey.EDIT, + contentDescription = "打开红外设置", onClick = onOpenSettings, modifier = Modifier.testTag("remote_settings_button"), - ) { - Text("⚙️", fontSize = 20.sp) - } + ) }, ) }, diff --git a/android/feature/infrared/src/main/res/values-zh-rCN/strings.xml b/android/feature/infrared/src/main/res/values-zh-rCN/strings.xml index 0a6dae8..b6b5e04 100644 --- a/android/feature/infrared/src/main/res/values-zh-rCN/strings.xml +++ b/android/feature/infrared/src/main/res/values-zh-rCN/strings.xml @@ -35,12 +35,12 @@ 红外遥控器 录制中 学习模式 - 🔴 学习模式已开启:点击任意按键开始录制实体遥控器信号 - ❄️ 空调面板 - 📺 电视面板 + 学习模式已开启:点击任意按键开始录制实体遥控器信号 + 空调面板 + 电视面板 设置 - 🔴 学习模式已开启:点击任意按键开始录制 + 学习模式已开启:点击任意按键开始录制 正在录制【%1$s】(槽位 %2$s),请按下实体遥控器按键… 【%1$s】录制成功 按键【%1$s】尚未学习,开启右上角学习模式即可录制 @@ -51,8 +51,8 @@ 温度调节 音量 频道 - ❄️ 制冷模式 - 💨 自动风速 | ↔ 自动扫风 + 制冷模式 + 自动风速 | 自动扫风 空调-电源 空调-升温 @@ -98,10 +98,10 @@ 静态占用:%1$d 动态缓冲:Slot %1$s 虚拟遥控器 - 暂无虚拟遥控器,点击右上角或下方 ➕ 添加 + 暂无虚拟遥控器,请使用添加操作 添加遥控器 - 🚀 静态独占(%1$s 槽位) - 📦 动态缓冲(共用 Slot %1$s) + 静态独占(%1$s 槽位) + 动态缓冲(共用槽位 %1$s) 添加虚拟遥控器 设备类型 预设码库 / 品牌 @@ -110,8 +110,8 @@ 节省槽位(0 额外槽位),现场写码发射(~80ms) 硬件槽位不足,请选择动态写码模式 硬件槽位与底层按键管理 - 📺 电视机 / 电视盒子 - ❄️ 空调设备 + 电视机 / 电视盒子 + 空调设备 🌀 电风扇 / 空气净化器 - 🎛️ 自定义面板 + 自定义面板 diff --git a/android/feature/infrared/src/main/res/values/strings.xml b/android/feature/infrared/src/main/res/values/strings.xml index e9a5a6f..9deb20c 100644 --- a/android/feature/infrared/src/main/res/values/strings.xml +++ b/android/feature/infrared/src/main/res/values/strings.xml @@ -36,12 +36,12 @@ Recording My Remotes Learn Mode - 🔴 Learn mode active: tap any button to record - ❄️ Air Conditioner - 📺 Television + Learn mode active: tap any button to record + Air Conditioner + Television Settings - 🔴 Learn mode active: tap any button to record + Learn mode active: tap any button to record Recording [%1$s] (Slot %2$s), press remote button… [%1$s] recorded successfully Button [%1$s] is not learned. Enable learn mode to record. @@ -52,8 +52,8 @@ Temp Control VOL CH - ❄️ Cool Mode - 💨 Auto Fan | ↔ Swing + Cool Mode + Auto Fan | Swing AC Power AC Temp + @@ -100,8 +100,8 @@ Virtual Remotes No virtual remotes added yet. Tap + to add one. Add Remote - 🚀 Static Mode (%1$s slots) - 📦 Dynamic Mode (Scratch Slot %1$s) + Static Mode (%1$s slots) + Dynamic Mode (Scratch Slot %1$s) Add Virtual Remote Device Type Brand / Preset Library @@ -110,8 +110,8 @@ Saves slots (0 extra slots), writes code on-the-fly (~80ms) Slot quota exceeded. Please choose Dynamic mode. Hardware Slot / Raw Key Management - 📺 Television (TV) - ❄️ Air Conditioner (AC) + Television (TV) + Air Conditioner (AC) 🌀 Fan / Air Purifier - 🎛️ Custom Panel + Custom Panel diff --git a/testing/fakes/src/main/kotlin/com/flagship/abox/manager/testing/fakes/FakeVirtualRemoteRepository.kt b/testing/fakes/src/main/kotlin/com/flagship/abox/manager/testing/fakes/FakeVirtualRemoteRepository.kt index ac183cc..a0b2acf 100644 --- a/testing/fakes/src/main/kotlin/com/flagship/abox/manager/testing/fakes/FakeVirtualRemoteRepository.kt +++ b/testing/fakes/src/main/kotlin/com/flagship/abox/manager/testing/fakes/FakeVirtualRemoteRepository.kt @@ -12,6 +12,7 @@ import com.flagship.abox.manager.domain.infrared.VirtualRemoteRepository import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.update import java.util.concurrent.ConcurrentHashMap class FakeVirtualRemoteRepository : VirtualRemoteRepository { @@ -80,13 +81,13 @@ class FakeVirtualRemoteRepository : VirtualRemoteRepository { ): DomainResult { remotes[remote.id] = remote bindings[remote.id] = initialBindings.toMutableList() - changeTrigger.value = System.currentTimeMillis() + changeTrigger.update { it + 1L } return DomainResult.Success(remote.id) } override suspend fun updateRemote(remote: VirtualRemote): DomainResult { remotes[remote.id] = remote - changeTrigger.value = System.currentTimeMillis() + changeTrigger.update { it + 1L } return DomainResult.Success(Unit) } @@ -96,7 +97,7 @@ class FakeVirtualRemoteRepository : VirtualRemoteRepository { ): DomainResult { remotes.remove(remoteId) bindings.remove(remoteId) - changeTrigger.value = System.currentTimeMillis() + changeTrigger.update { it + 1L } return DomainResult.Success(Unit) } @@ -109,7 +110,7 @@ class FakeVirtualRemoteRepository : VirtualRemoteRepository { list.add(binding) } bindings[binding.remoteId] = list - changeTrigger.value = System.currentTimeMillis() + changeTrigger.update { it + 1L } return DomainResult.Success(Unit) } @@ -120,7 +121,7 @@ class FakeVirtualRemoteRepository : VirtualRemoteRepository { val list = bindings[remoteId] ?: return DomainResult.Success(Unit) list.removeAll { it.buttonId == buttonId } bindings[remoteId] = list - changeTrigger.value = System.currentTimeMillis() + changeTrigger.update { it + 1L } return DomainResult.Success(Unit) } @@ -131,7 +132,7 @@ class FakeVirtualRemoteRepository : VirtualRemoteRepository { ): DomainResult { val existing = remotes[remoteId] ?: return DomainResult.Success(Unit) remotes[remoteId] = existing.copy(acState = state) - changeTrigger.value = System.currentTimeMillis() + changeTrigger.update { it + 1L } return DomainResult.Success(Unit) } }