Explorar el Código

add translation for German and Italian

master
gituser hace 2 semanas
padre
commit
83909fd379

+ 17
- 14
app/src/main/java/ch/spherIC/ledlampcontrol/data/network/LedDiscoveryManager.kt Ver fichero

@@ -12,6 +12,14 @@ import kotlinx.coroutines.flow.update

class LedDiscoveryManager(context: Context) {

sealed class ScanStatus {
object Idle : ScanStatus()
object Scanning : ScanStatus()
data class Found(val name: String) : ScanStatus()
data class Error(val message: String? = null) : ScanStatus()
object Failed : ScanStatus()
}

private val nsdManager = context.getSystemService(Context.NSD_SERVICE) as NsdManager
private val wifiManager = context.applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager
private var multicastLock: WifiManager.MulticastLock? = null
@@ -19,31 +27,27 @@ class LedDiscoveryManager(context: Context) {
private val _discoveredDevices = MutableStateFlow<List<LedDevice>>(emptyList())
val discoveredDevices: StateFlow<List<LedDevice>> = _discoveredDevices.asStateFlow()

private val _scanStatus = MutableStateFlow("Idle")
val scanStatus: StateFlow<String> = _scanStatus.asStateFlow()
private val _scanStatus = MutableStateFlow<ScanStatus>(ScanStatus.Idle)
val scanStatus: StateFlow<ScanStatus> = _scanStatus.asStateFlow()

private val _isScanning = MutableStateFlow(false)
val isScanning: StateFlow<Boolean> = _isScanning.asStateFlow()

// Service type from your ESP8266
private val SERVICE_TYPE = "_LEDLamp._tcp"
private val resolvedNames = mutableSetOf<String>()

private val discoveryListener = object : NsdManager.DiscoveryListener {
override fun onDiscoveryStarted(regType: String) {
_scanStatus.value = "Scanning..."
_scanStatus.value = ScanStatus.Scanning
_isScanning.value = true
}

override fun onServiceFound(service: NsdServiceInfo) {
// Prevent duplicate resolve requests for the same name
if (resolvedNames.contains(service.serviceName)) return
_scanStatus.value = "Found: ${service.serviceName}"
nsdManager.resolveService(service, object : NsdManager.ResolveListener {
override fun onResolveFailed(serviceInfo: NsdServiceInfo, errorCode: Int) {
_scanStatus.value = "Error resolving ${serviceInfo.serviceName}"
_scanStatus.value = ScanStatus.Error("Resolve failed: $errorCode")
}

override fun onServiceResolved(serviceInfo: NsdServiceInfo) {
@@ -51,7 +55,6 @@ class LedDiscoveryManager(context: Context) {
if (ip.isNotBlank() && !resolvedNames.contains(serviceInfo.serviceName)) {
resolvedNames.add(serviceInfo.serviceName)
// Extract name from TXT record if available, else use service name
val displayName = serviceInfo.attributes["name"]?.let { String(it) }
?: serviceInfo.serviceName

@@ -62,7 +65,7 @@ class LedDiscoveryManager(context: Context) {
port = 81
)
_discoveredDevices.update { (it + device).distinctBy { d -> d.ip } }
_scanStatus.value = "Discovered: $displayName"
_scanStatus.value = ScanStatus.Found(displayName)
}
}
})
@@ -76,12 +79,12 @@ class LedDiscoveryManager(context: Context) {
}

override fun onDiscoveryStopped(regType: String) {
_scanStatus.value = "Idle"
_scanStatus.value = ScanStatus.Idle
_isScanning.value = false
}

override fun onStartDiscoveryFailed(regType: String, errorCode: Int) {
_scanStatus.value = "Failed to start"
_scanStatus.value = ScanStatus.Failed
_isScanning.value = false
stopDiscovery()
}
@@ -105,7 +108,7 @@ class LedDiscoveryManager(context: Context) {
try {
nsdManager.discoverServices(SERVICE_TYPE, NsdManager.PROTOCOL_DNS_SD, discoveryListener)
} catch (e: Exception) {
_scanStatus.value = "Discovery error"
_scanStatus.value = ScanStatus.Error(e.message)
_isScanning.value = false
}
}
@@ -114,7 +117,7 @@ class LedDiscoveryManager(context: Context) {
try {
nsdManager.stopServiceDiscovery(discoveryListener)
} catch (e: Exception) {
// Already stopped or not started
// Already stopped
}
_isScanning.value = false
if (multicastLock?.isHeld == true) {

+ 92
- 75
app/src/main/java/ch/spherIC/ledlampcontrol/ui/dashboard/DashboardScreen.kt Ver fichero

@@ -15,9 +15,12 @@ import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import ch.spherIC.ledlampcontrol.R
import ch.spherIC.ledlampcontrol.data.model.*
import ch.spherIC.ledlampcontrol.data.network.LedDiscoveryManager
import ch.spherIC.ledlampcontrol.data.network.LedWebSocketManager
import ch.spherIC.ledlampcontrol.ui.components.ColorWheel
import ch.spherIC.ledlampcontrol.ui.theme.LEDLampControlTheme
@@ -40,18 +43,18 @@ fun DashboardScreen(viewModel: LedViewModel) {
drawerState = drawerState,
drawerContent = {
ModalDrawerSheet {
Text("Select Device", modifier = Modifier.padding(16.dp), style = MaterialTheme.typography.titleLarge)
Text(stringResource(R.string.drawer_title), modifier = Modifier.padding(16.dp), style = MaterialTheme.typography.titleLarge)
HorizontalDivider()
devices.forEach { device ->
val isSelected = selectedDeviceId == device.id
val statusText = if (isSelected) {
when (connectionState) {
is LedWebSocketManager.ConnectionState.Connected -> "Connected"
is LedWebSocketManager.ConnectionState.Connecting -> "Connecting..."
is LedWebSocketManager.ConnectionState.Disconnected -> "Disconnected"
is LedWebSocketManager.ConnectionState.Error -> "Connection failed"
is LedWebSocketManager.ConnectionState.Connected -> stringResource(R.string.status_connected)
is LedWebSocketManager.ConnectionState.Connecting -> stringResource(R.string.status_connecting)
is LedWebSocketManager.ConnectionState.Disconnected -> stringResource(R.string.status_disconnected)
is LedWebSocketManager.ConnectionState.Error -> stringResource(R.string.status_failed)
}
} else "Inactive"
} else stringResource(R.string.status_inactive)

val statusColor = if (isSelected) {
when (connectionState) {
@@ -81,7 +84,7 @@ fun DashboardScreen(viewModel: LedViewModel) {
}
Spacer(modifier = Modifier.weight(1f))
NavigationDrawerItem(
label = { Text("Manage Devices") },
label = { Text(stringResource(R.string.drawer_manage_devices)) },
selected = selectedTab == 3,
onClick = {
selectedTab = 3
@@ -98,7 +101,7 @@ fun DashboardScreen(viewModel: LedViewModel) {
CenterAlignedTopAppBar(
title = {
val currentDevice = devices.find { it.id == selectedDeviceId }
Text(currentDevice?.name ?: "LED Control")
Text(currentDevice?.name ?: stringResource(R.string.app_name))
},
navigationIcon = {
IconButton(onClick = { scope.launch { drawerState.open() } }) {
@@ -123,32 +126,32 @@ fun DashboardScreen(viewModel: LedViewModel) {
NavigationBarItem(
selected = selectedTab == 0,
onClick = { selectedTab = 0 },
icon = { Icon(Icons.Default.Tune, contentDescription = "Adjust") },
label = { Text("Adjust") }
icon = { Icon(Icons.Default.Tune, contentDescription = stringResource(R.string.tab_adjust)) },
label = { Text(stringResource(R.string.tab_adjust)) }
)
NavigationBarItem(
selected = selectedTab == 1,
onClick = { selectedTab = 1 },
icon = { Icon(Icons.Default.GridView, contentDescription = "Modes") },
label = { Text("Modes") }
icon = { Icon(Icons.Default.GridView, contentDescription = stringResource(R.string.tab_modes)) },
label = { Text(stringResource(R.string.tab_modes)) }
)
NavigationBarItem(
selected = selectedTab == 2,
onClick = { selectedTab = 2 },
icon = { Icon(Icons.Default.Timer, contentDescription = "Timer") },
label = { Text("Timer") }
icon = { Icon(Icons.Default.Timer, contentDescription = stringResource(R.string.tab_timer)) },
label = { Text(stringResource(R.string.tab_timer)) }
)
NavigationBarItem(
selected = selectedTab == 3,
onClick = { selectedTab = 3 },
icon = { Icon(Icons.Default.Router, contentDescription = "Devices") },
label = { Text("Devices") }
icon = { Icon(Icons.Default.Router, contentDescription = stringResource(R.string.tab_devices)) },
label = { Text(stringResource(R.string.tab_devices)) }
)
NavigationBarItem(
selected = selectedTab == 4,
onClick = { selectedTab = 4 },
icon = { Icon(Icons.Default.Palette, contentDescription = "Style") },
label = { Text("Style") }
icon = { Icon(Icons.Default.Palette, contentDescription = stringResource(R.string.tab_style)) },
label = { Text(stringResource(R.string.tab_style)) }
)
}
}
@@ -172,8 +175,6 @@ fun DashboardScreen(viewModel: LedViewModel) {
if (w > 0) {
viewModel.updateColor("singleColor", 0, 0, 0, w)
} else {
// If W is set to 0, just send 0,0,0,0 or stay as is?
// Based on requirements, if W > 0, RGB must be 0.
viewModel.updateColor("singleColor", 0, 0, 0, 0)
}
},
@@ -241,7 +242,6 @@ fun AdjustPane(
var localBrightness by remember(ledConfig.brightness) { mutableFloatStateOf(ledConfig.brightness.toFloat()) }
var localWhite by remember(ledConfig.singleColor.w) { mutableFloatStateOf(ledConfig.singleColor.w.toFloat()) }

// When an RGB color is picked (wheel or presets), we must reset the local white slider
val handleRgbChange: (Int, Int, Int) -> Unit = { r, g, b ->
localWhite = 0f
onColorChanged(r, g, b)
@@ -259,7 +259,7 @@ fun AdjustPane(
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Text("Power", style = MaterialTheme.typography.titleLarge)
Text(stringResource(R.string.label_power), style = MaterialTheme.typography.titleLarge)
Switch(
checked = ledState.power,
onCheckedChange = { onPowerToggle() }
@@ -278,8 +278,7 @@ fun AdjustPane(

Spacer(modifier = Modifier.height(24.dp))

// Brightness Slider
Text("Brightness: ${localBrightness.toInt()}", style = MaterialTheme.typography.titleMedium)
Text(stringResource(R.string.label_brightness, localBrightness.toInt()), style = MaterialTheme.typography.titleMedium)
Slider(
value = localBrightness,
onValueChange = {
@@ -292,8 +291,7 @@ fun AdjustPane(

Spacer(modifier = Modifier.height(8.dp))

// White Channel Slider
Text("White Channel: ${localWhite.toInt()}", style = MaterialTheme.typography.titleMedium)
Text(stringResource(R.string.label_white_channel, localWhite.toInt()), style = MaterialTheme.typography.titleMedium)
Slider(
value = localWhite,
onValueChange = {
@@ -307,7 +305,7 @@ fun AdjustPane(
Spacer(modifier = Modifier.height(16.dp))

PresetColorSection(
title = "Preset",
title = stringResource(R.string.label_preset),
items = listOf(
ColorItem(Color(0xFF4CAF50)), // Green
ColorItem(Color(0xFFE91E63)), // Magenta
@@ -329,7 +327,7 @@ fun AdjustPane(
Spacer(modifier = Modifier.height(16.dp))

PresetColorSection(
title = "Classic",
title = stringResource(R.string.label_classic),
items = listOf(
ColorItem(Color.Yellow),
ColorItem(Color.White),
@@ -364,11 +362,19 @@ fun StylePane(
.verticalScroll(rememberScrollState()),
horizontalAlignment = Alignment.CenterHorizontally
) {
Text("Animated Modes", style = MaterialTheme.typography.headlineMedium)
Text(stringResource(R.string.title_modes), style = MaterialTheme.typography.headlineMedium)
Spacer(modifier = Modifier.height(24.dp))
LedModeType.entries.forEach { mode ->
val displayName = mode.name.replace("_", " ").lowercase().replaceFirstChar { it.uppercase() }
val stringId = when (mode) {
LedModeType.SINGLE_COLOR -> R.string.mode_single_color
LedModeType.RAINBOW -> R.string.mode_rainbow
LedModeType.SPECTRAL -> R.string.mode_spectral
LedModeType.FADING -> R.string.mode_fading
LedModeType.FLASH -> R.string.mode_flash
LedModeType.SEGMENTS -> R.string.mode_segments
}
val displayName = stringResource(stringId)
NavigationDrawerItem(
label = { Text(displayName) },
selected = ledConfig.mode == mode,
@@ -382,11 +388,10 @@ fun StylePane(
Spacer(modifier = Modifier.height(32.dp))

// Mode specific parameters
when (ledConfig.mode) {
LedModeType.RAINBOW -> {
ParamSlider(
label = "Rainbow Delay",
label = stringResource(R.string.param_rainbow_delay, ledConfig.rainbowDelay.toInt()),
value = ledConfig.rainbowDelay,
range = 0f..500f,
onValueChange = { onParamChanged("rainbowDelay", it.toString()) }
@@ -394,7 +399,7 @@ fun StylePane(
}
LedModeType.SPECTRAL -> {
ParamSlider(
label = "Spectral Step Time",
label = stringResource(R.string.param_spectral_step, ledConfig.spectralStepTime.toInt()),
value = ledConfig.spectralStepTime,
range = 100f..30000f,
onValueChange = { onParamChanged("spectralStepTime", it.toString()) }
@@ -402,13 +407,13 @@ fun StylePane(
}
LedModeType.FADING -> {
ParamSlider(
label = "Hold Time",
label = stringResource(R.string.param_fading_hold, ledConfig.fadingHoldTime.toInt()),
value = ledConfig.fadingHoldTime,
range = 100f..10000f,
onValueChange = { onParamChanged("fadingHoldTime", it.toString()) }
)
ParamSlider(
label = "Fade Time",
label = stringResource(R.string.param_fading_fade, ledConfig.fadingFadeTime.toInt()),
value = ledConfig.fadingFadeTime,
range = 100f..5000f,
onValueChange = { onParamChanged("fadingFadeTime", it.toString()) }
@@ -416,24 +421,24 @@ fun StylePane(
}
LedModeType.FLASH -> {
ParamSlider(
label = "On Time",
label = stringResource(R.string.param_flash_on, ledConfig.flashOnTime.toInt()),
value = ledConfig.flashOnTime,
range = 100f..5000f,
onValueChange = { onParamChanged("flashOnTime", it.toString()) }
)
ParamSlider(
label = "Off Time",
label = stringResource(R.string.param_flash_off, ledConfig.flashOffTime.toInt()),
value = ledConfig.flashOffTime,
range = 100f..5000f,
onValueChange = { onParamChanged("flashOffTime", it.toString()) }
)
}
LedModeType.SEGMENTS -> {
SegmentColorPicker("Segment 1", "segmentColor1", ledConfig.segmentColor1, onSegmentColorChanged)
SegmentColorPicker(stringResource(R.string.label_segment, 1), "segmentColor1", ledConfig.segmentColor1, onSegmentColorChanged)
Spacer(modifier = Modifier.height(16.dp))
SegmentColorPicker("Segment 2", "segmentColor2", ledConfig.segmentColor2, onSegmentColorChanged)
SegmentColorPicker(stringResource(R.string.label_segment, 2), "segmentColor2", ledConfig.segmentColor2, onSegmentColorChanged)
Spacer(modifier = Modifier.height(16.dp))
SegmentColorPicker("Segment 3", "segmentColor3", ledConfig.segmentColor3, onSegmentColorChanged)
SegmentColorPicker(stringResource(R.string.label_segment, 3), "segmentColor3", ledConfig.segmentColor3, onSegmentColorChanged)
}
else -> {}
}
@@ -445,7 +450,7 @@ fun ParamSlider(label: String, value: Long, range: ClosedFloatingPointRange<Floa
var localValue by remember(value) { mutableFloatStateOf(value.toFloat()) }
Column {
Text("$label: ${localValue.toInt()}ms", style = MaterialTheme.typography.titleMedium)
Text(label, style = MaterialTheme.typography.titleMedium)
Slider(
value = localValue,
onValueChange = {
@@ -483,10 +488,9 @@ fun SegmentColorPicker(label: String, key: String, color: LedColor, onColorChang
if (showDialog) {
AlertDialog(
onDismissRequest = { showDialog = false },
title = { Text("Select Color for $label") },
title = { Text(stringResource(R.string.btn_change_color) + ": " + label) },
text = {
Column {
// Simple color wheel or grid here. For now, a few presets.
val presets = listOf(Color.Red, Color.Green, Color.Blue, Color.Yellow, Color.Magenta, Color.Cyan, Color.White)
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceEvenly) {
presets.forEach { preset ->
@@ -509,7 +513,7 @@ fun SegmentColorPicker(label: String, key: String, color: LedColor, onColorChang
}
}
},
confirmButton = { TextButton(onClick = { showDialog = false }) { Text("Close") } }
confirmButton = { TextButton(onClick = { showDialog = false }) { Text(stringResource(R.string.btn_cancel)) } }
)
}
}
@@ -527,7 +531,7 @@ fun TimerPane(
.verticalScroll(rememberScrollState()),
horizontalAlignment = Alignment.CenterHorizontally
) {
Text("Timer Settings", style = MaterialTheme.typography.headlineMedium)
Text(stringResource(R.string.title_timer), style = MaterialTheme.typography.headlineMedium)
Spacer(modifier = Modifier.height(24.dp))

Row(
@@ -535,17 +539,17 @@ fun TimerPane(
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Text("Enable Timer", style = MaterialTheme.typography.titleLarge)
Text(stringResource(R.string.label_enable_timer), style = MaterialTheme.typography.titleLarge)
Switch(checked = timer.on, onCheckedChange = { onToggle() })
}

Spacer(modifier = Modifier.height(32.dp))

TimerIntervalItem("Interval 1", "timeInterval1", timer.timeInterval1, onIntervalChange)
TimerIntervalItem(stringResource(R.string.label_interval_1), "timeInterval1", timer.timeInterval1, onIntervalChange)
Spacer(modifier = Modifier.height(24.dp))

TimerIntervalItem("Interval 2", "timeInterval2", timer.timeInterval2, onIntervalChange)
TimerIntervalItem(stringResource(R.string.label_interval_2), "timeInterval2", timer.timeInterval2, onIntervalChange)
}
}

@@ -558,10 +562,10 @@ fun TimerIntervalItem(label: String, key: String, interval: TimeInterval, onUpda
Column(modifier = Modifier.padding(16.dp)) {
Text(label, style = MaterialTheme.typography.titleMedium)
Spacer(modifier = Modifier.height(8.dp))
Text("From: ${formatSeconds(interval.start)} To: ${formatSeconds(interval.end)}")
Text(stringResource(R.string.label_time_range, formatSeconds(interval.start), formatSeconds(interval.end)))
Spacer(modifier = Modifier.height(16.dp))
Button(onClick = { showDialog = true }) {
Text("Edit Range")
Text(stringResource(R.string.btn_edit_range))
}
}
}
@@ -605,15 +609,15 @@ fun TimeRangePickerDialog(

AlertDialog(
onDismissRequest = onDismiss,
title = { Text("Set Time Range for $label") },
title = { Text(stringResource(R.string.dialog_timer_title, label)) },
text = {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
TabRow(selectedTabIndex = if (pickingStart) 0 else 1) {
Tab(selected = pickingStart, onClick = { pickingStart = true }) {
Text("Start Time", modifier = Modifier.padding(16.dp))
Text(stringResource(R.string.label_start_time), modifier = Modifier.padding(16.dp))
}
Tab(selected = !pickingStart, onClick = { pickingStart = false }) {
Text("End Time", modifier = Modifier.padding(16.dp))
Text(stringResource(R.string.label_end_time), modifier = Modifier.padding(16.dp))
}
}
@@ -632,12 +636,12 @@ fun TimeRangePickerDialog(
val endSeconds = endState.hour * 3600L + endState.minute * 60L
onConfirm(startSeconds, endSeconds)
}) {
Text("Confirm")
Text(stringResource(R.string.btn_confirm))
}
},
dismissButton = {
TextButton(onClick = onDismiss) {
Text("Cancel")
Text(stringResource(R.string.btn_cancel))
}
}
)
@@ -654,7 +658,7 @@ private fun formatSeconds(seconds: Long): String {
fun DevicesPane(
savedDevices: List<LedDevice>,
discoveredDevices: List<LedDevice>,
scanStatus: String,
scanStatus: LedDiscoveryManager.ScanStatus,
isScanning: Boolean,
onAddDevice: (String, String) -> Unit,
onRemoveDevice: (String) -> Unit,
@@ -670,6 +674,14 @@ fun DevicesPane(
}
}

val statusString = when (scanStatus) {
is LedDiscoveryManager.ScanStatus.Idle -> stringResource(R.string.scan_idle)
is LedDiscoveryManager.ScanStatus.Scanning -> stringResource(R.string.scan_scanning)
is LedDiscoveryManager.ScanStatus.Found -> stringResource(R.string.scan_found, scanStatus.name)
is LedDiscoveryManager.ScanStatus.Error -> stringResource(R.string.scan_error) + (scanStatus.message?.let { ": $it" } ?: "")
is LedDiscoveryManager.ScanStatus.Failed -> stringResource(R.string.scan_failed)
}

Column(
modifier = Modifier
.fillMaxSize()
@@ -677,15 +689,15 @@ fun DevicesPane(
.verticalScroll(rememberScrollState()),
horizontalAlignment = Alignment.CenterHorizontally
) {
Text("Manage Devices", style = MaterialTheme.typography.headlineMedium)
Text("Scanner Status: $scanStatus", style = MaterialTheme.typography.bodySmall, color = Color.Gray)
Text(stringResource(R.string.title_devices), style = MaterialTheme.typography.headlineMedium)
Text(stringResource(R.string.status_scanner, statusString), style = MaterialTheme.typography.bodySmall, color = Color.Gray)
Spacer(modifier = Modifier.height(16.dp))
if (isScanning) {
CircularProgressIndicator(modifier = Modifier.size(32.dp))
Spacer(modifier = Modifier.height(8.dp))
Text("Searching for LED strips...", style = MaterialTheme.typography.bodySmall)
Text(stringResource(R.string.status_searching), style = MaterialTheme.typography.bodySmall)
}

Spacer(modifier = Modifier.height(16.dp))
@@ -702,26 +714,26 @@ fun DevicesPane(
) {
Icon(if (isScanning) Icons.Default.Stop else Icons.Default.Search, contentDescription = null)
Spacer(modifier = Modifier.width(8.dp))
Text(if (isScanning) "Stop Scanning" else "Scan for Devices")
Text(if (isScanning) stringResource(R.string.btn_stop_scan) else stringResource(R.string.btn_scan))
}

Spacer(modifier = Modifier.height(24.dp))

Card(modifier = Modifier.fillMaxWidth()) {
Column(modifier = Modifier.padding(16.dp)) {
Text("Add Manual Device", style = MaterialTheme.typography.titleMedium)
Text(stringResource(R.string.label_add_manual), style = MaterialTheme.typography.titleMedium)
Spacer(modifier = Modifier.height(8.dp))
OutlinedTextField(
value = nameText,
onValueChange = { nameText = it },
label = { Text("Name") },
label = { Text(stringResource(R.string.label_name)) },
modifier = Modifier.fillMaxWidth()
)
OutlinedTextField(
value = ipText,
onValueChange = { ipText = it },
label = { Text("IP Address") },
placeholder = { Text("e.g. 192.168.1.100 (Port 81 is default)") },
label = { Text(stringResource(R.string.label_ip_address)) },
placeholder = { Text(stringResource(R.string.placeholder_ip)) },
modifier = Modifier.fillMaxWidth()
)
Spacer(modifier = Modifier.height(8.dp))
@@ -735,7 +747,7 @@ fun DevicesPane(
},
modifier = Modifier.align(Alignment.End)
) {
Text("Add")
Text(stringResource(R.string.btn_add))
}
}
}
@@ -743,7 +755,7 @@ fun DevicesPane(
Spacer(modifier = Modifier.height(24.dp))

if (discoveredDevices.isNotEmpty()) {
Text("Discovered Devices", style = MaterialTheme.typography.titleMedium)
Text(stringResource(R.string.title_discovered), style = MaterialTheme.typography.titleMedium)
discoveredDevices.forEach { device ->
var editedName by remember(device.id) { mutableStateOf(device.name) }
Card(
@@ -762,7 +774,7 @@ fun DevicesPane(
OutlinedTextField(
value = editedName,
onValueChange = { editedName = it },
label = { Text("Device Name") },
label = { Text(stringResource(R.string.label_device_name)) },
singleLine = true,
modifier = Modifier.fillMaxWidth()
)
@@ -781,7 +793,7 @@ fun DevicesPane(
contentColor = MaterialTheme.colorScheme.onPrimaryContainer
)
) {
Icon(Icons.Default.Add, contentDescription = "Add")
Icon(Icons.Default.Add, contentDescription = stringResource(R.string.btn_add))
}
}
}
@@ -789,7 +801,7 @@ fun DevicesPane(
Spacer(modifier = Modifier.height(24.dp))
}

Text("Saved Devices", style = MaterialTheme.typography.titleMedium)
Text(stringResource(R.string.title_saved), style = MaterialTheme.typography.titleMedium)
savedDevices.forEach { device ->
ListItem(
headlineContent = { Text(device.name) },
@@ -820,15 +832,20 @@ fun AppearancePane(
.verticalScroll(rememberScrollState()),
horizontalAlignment = Alignment.CenterHorizontally
) {
Text("Style", style = MaterialTheme.typography.headlineMedium)
Text(stringResource(R.string.tab_style), style = MaterialTheme.typography.headlineMedium)
Spacer(modifier = Modifier.height(24.dp))

Text("Theme Mode", style = MaterialTheme.typography.titleMedium)
Text(stringResource(R.string.label_theme_mode), style = MaterialTheme.typography.titleMedium)
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween
) {
listOf("System", "Light", "Dark").forEachIndexed { index, label ->
val themeLabels = listOf(
stringResource(R.string.theme_system),
stringResource(R.string.theme_light),
stringResource(R.string.theme_dark)
)
themeLabels.forEachIndexed { index, label ->
FilterChip(
selected = themeMode == index,
onClick = { onThemeChange(index) },
@@ -845,8 +862,8 @@ fun AppearancePane(
horizontalArrangement = Arrangement.SpaceBetween
) {
Column {
Text("Dynamic Color", style = MaterialTheme.typography.titleMedium)
Text("Use wallpaper colors (Android 12+)", style = MaterialTheme.typography.bodySmall)
Text(stringResource(R.string.label_dynamic_color), style = MaterialTheme.typography.titleMedium)
Text(stringResource(R.string.desc_dynamic_color), style = MaterialTheme.typography.bodySmall)
}
Switch(
checked = dynamicColor,
@@ -921,7 +938,7 @@ fun DevicesPanePreview() {
discoveredDevices = listOf(
LedDevice("3", "Kitchen", "192.168.1.103")
),
scanStatus = "Scanning...",
scanStatus = LedDiscoveryManager.ScanStatus.Scanning,
isScanning = true,
onAddDevice = { _, _ -> },
onRemoveDevice = { _ -> },

+ 1
- 1
app/src/main/java/ch/spherIC/ledlampcontrol/ui/viewmodel/LedViewModel.kt Ver fichero

@@ -38,7 +38,7 @@ class LedViewModel(
}
}.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), emptyList())

val scanStatus: StateFlow<String> = discoveryManager.scanStatus
val scanStatus: StateFlow<LedDiscoveryManager.ScanStatus> = discoveryManager.scanStatus
val isScanning: StateFlow<Boolean> = discoveryManager.isScanning

val themeMode: StateFlow<Int> = settingsRepository.themeModeFlow

+ 89
- 0
app/src/main/res/values-de/strings.xml Ver fichero

@@ -0,0 +1,89 @@
<resources>
<string name="app_name">LEDLamp Control</string>

<!-- Navigation -->
<string name="tab_adjust">Anpassen</string>
<string name="tab_modes">Modi</string>
<string name="tab_timer">Timer</string>
<string name="tab_devices">Geräte</string>
<string name="tab_style">Stil</string>

<!-- Drawer -->
<string name="drawer_title">Gerät auswählen</string>
<string name="drawer_manage_devices">Geräte verwalten</string>
<string name="status_connected">Verbunden</string>
<string name="status_connecting">Verbinde…</string>
<string name="status_disconnected">Getrennt</string>
<string name="status_failed">Verbindung fehlgeschlagen</string>
<string name="status_inactive">Inaktiv</string>

<!-- Adjust Pane -->
<string name="label_power">Ein-/Ausschalten</string>
<string name="label_brightness">Helligkeit: %1$d</string>
<string name="label_white_channel">Weiß-Kanal: %1$d</string>
<string name="label_preset">Voreinstellung</string>
<string name="label_classic">Klassisch</string>

<!-- Modes Pane -->
<string name="title_modes">Modi</string>
<string name="label_speed">Animationsgeschwindigkeit</string>
<string name="param_rainbow_delay">Verzögerung: %1$dms</string>
<string name="param_spectral_step">Spektralschritt-Zeit: %1$dms</string>
<string name="param_fading_hold">Haltezeit: %1$dms</string>
<string name="param_fading_fade">Überblendzeit: %1$dms</string>
<string name="param_flash_on">An-Zeit: %1$dms</string>
<string name="param_flash_off">Aus-Zeit: %1$dms</string>
<string name="label_segment">Segment %1$d</string>
<string name="btn_change_color">Farbe ändern</string>

<!-- Timer Pane -->
<string name="title_timer">Timer-Einstellungen</string>
<string name="label_enable_timer">Timer aktivieren</string>
<string name="label_interval_1">Intervall 1</string>
<string name="label_interval_2">Intervall 2</string>
<string name="label_time_range">Von: %1$s Bis: %2$s</string>
<string name="btn_edit_range">Bereich bearbeiten</string>
<string name="dialog_timer_title">Zeitbereich für %1$s festlegen</string>
<string name="label_start_time">Startzeit</string>
<string name="label_end_time">Endzeit</string>
<string name="btn_confirm">Bestätigen</string>
<string name="btn_cancel">Abbrechen</string>

<!-- Devices Pane -->
<string name="title_devices">Geräte verwalten</string>
<string name="status_scanner">Scanner-Status: %1$s</string>
<string name="status_searching">Suche nach LED-Strips…</string>
<string name="btn_scan">Nach Geräten suchen</string>
<string name="btn_stop_scan">Suche stoppen</string>
<string name="label_add_manual">Manuell hinzufügen</string>
<string name="label_name">Name</string>
<string name="label_ip_address">IP-Adresse</string>
<string name="placeholder_ip">z.B. 192.168.1.100 (Port 81 ist Standard)</string>
<string name="btn_add">Hinzufügen</string>
<string name="title_discovered">Entdeckte Geräte</string>
<string name="title_saved">Gespeicherte Geräte</string>
<string name="label_device_name">Gerätename</string>

<!-- Style Pane -->
<string name="title_style">Stil</string>
<string name="label_theme_mode">Design-Modus</string>
<string name="theme_system">System</string>
<string name="theme_light">Hell</string>
<string name="theme_dark">Dunkel</string>
<string name="label_dynamic_color">Dynamische Farbe</string>
<string name="desc_dynamic_color">Hintergrundfarben verwenden (Android 12+)</string>

<!-- Scan Statuses -->
<string name="scan_idle">Bereit</string>
<string name="scan_scanning">Suche läuft…</string>
<string name="scan_found">Gefunden: %1$s</string>
<string name="scan_error">Fehler bei Suche</string>
<string name="scan_failed">Start fehlgeschlagen</string>
<!-- Modes -->
<string name="mode_single_color">Einfarbiger Modus</string>
<string name="mode_rainbow">Regenbogen</string>
<string name="mode_spectral">Spektral</string>
<string name="mode_fading">Überblenden</string>
<string name="mode_flash">Blinken</string>
<string name="mode_segments">Segmente</string>
</resources>

+ 89
- 0
app/src/main/res/values-it/strings.xml Ver fichero

@@ -0,0 +1,89 @@
<resources>
<string name="app_name">LEDLamp Control</string>

<!-- Navigation -->
<string name="tab_adjust">Regola</string>
<string name="tab_modes">Modalità</string>
<string name="tab_timer">Timer</string>
<string name="tab_devices">Dispositivi</string>
<string name="tab_style">Stile</string>

<!-- Drawer -->
<string name="drawer_title">Seleziona Dispositivo</string>
<string name="drawer_manage_devices">Gestisci Dispositivi</string>
<string name="status_connected">Connesso</string>
<string name="status_connecting">Connessione in corso…</string>
<string name="status_disconnected">Disconnesso</string>
<string name="status_failed">Connessione fallita</string>
<string name="status_inactive">Inattivo</string>

<!-- Adjust Pane -->
<string name="label_power">Alimentazione</string>
<string name="label_brightness">Luminosità: %1$d</string>
<string name="label_white_channel">Canale Bianco: %1$d</string>
<string name="label_preset">Preimpostato</string>
<string name="label_classic">Classico</string>

<!-- Modes Pane -->
<string name="title_modes">Modalità</string>
<string name="label_speed">Velocità animazione</string>
<string name="param_rainbow_delay">Ritardo: %1$dms</string>
<string name="param_spectral_step">Tempo Passo Spettrale: %1$dms</string>
<string name="param_fading_hold">Tempo Tenuta: %1$dms</string>
<string name="param_fading_fade">Tempo Dissolvenza: %1$dms</string>
<string name="param_flash_on">Tempo Acceso: %1$dms</string>
<string name="param_flash_off">Tempo Spento: %1$dms</string>
<string name="label_segment">Segmento %1$d</string>
<string name="btn_change_color">Cambia Colore</string>

<!-- Timer Pane -->
<string name="title_timer">Impostazioni Timer</string>
<string name="label_enable_timer">Abilita Timer</string>
<string name="label_interval_1">Intervallo 1</string>
<string name="label_interval_2">Intervallo 2</string>
<string name="label_time_range">Da: %1$s A: %2$s</string>
<string name="btn_edit_range">Modifica Intervallo</string>
<string name="dialog_timer_title">Imposta Intervallo di Tempo per %1$s</string>
<string name="label_start_time">Ora di Inizio</string>
<string name="label_end_time">Ora di Fine</string>
<string name="btn_confirm">Conferma</string>
<string name="btn_cancel">Annulla</string>

<!-- Devices Pane -->
<string name="title_devices">Gestisci Dispositivi</string>
<string name="status_scanner">Stato Scanner: %1$s</string>
<string name="status_searching">Ricerca strisce LED in corso…</string>
<string name="btn_scan">Cerca Dispositivi</string>
<string name="btn_stop_scan">Ferma Ricerca</string>
<string name="label_add_manual">Aggiungi Dispositivo Manualmente</string>
<string name="label_name">Nome</string>
<string name="label_ip_address">Indirizzo IP</string>
<string name="placeholder_ip">es. 192.168.1.100 (Porta 81 predefinita)</string>
<string name="btn_add">Aggiungi</string>
<string name="title_discovered">Dispositivi Trovati</string>
<string name="title_saved">Dispositivi Salvati</string>
<string name="label_device_name">Nome Dispositivo</string>

<!-- Style Pane -->
<string name="title_style">Stile</string>
<string name="label_theme_mode">Modalità Tema</string>
<string name="theme_system">Sistema</string>
<string name="theme_light">Chiaro</string>
<string name="theme_dark">Scuro</string>
<string name="label_dynamic_color">Colore Dinamico</string>
<string name="desc_dynamic_color">Usa i colori dello sfondo (Android 12+)</string>

<!-- Scan Statuses -->
<string name="scan_idle">Inattivo</string>
<string name="scan_scanning">Ricerca in corso…</string>
<string name="scan_found">Trovato: %1$s</string>
<string name="scan_error">Errore di ricerca</string>
<string name="scan_failed">Avvio fallito</string>
<!-- Modes -->
<string name="mode_single_color">Colore singolo</string>
<string name="mode_rainbow">Arcobaleno</string>
<string name="mode_spectral">Spettrale</string>
<string name="mode_fading">Dissolvenza</string>
<string name="mode_flash">Lampeggio</string>
<string name="mode_segments">Segmenti</string>
</resources>

+ 87
- 1
app/src/main/res/values/strings.xml Ver fichero

@@ -1,3 +1,89 @@
<resources>
<string name="app_name">LEDLampControl</string>
<string name="app_name">LEDLamp Control</string>

<!-- Navigation -->
<string name="tab_adjust">Adjust</string>
<string name="tab_modes">Modes</string>
<string name="tab_timer">Timer</string>
<string name="tab_devices">Devices</string>
<string name="tab_style">Style</string>

<!-- Drawer -->
<string name="drawer_title">Select Device</string>
<string name="drawer_manage_devices">Manage Devices</string>
<string name="status_connected">Connected</string>
<string name="status_connecting">Connecting…</string>
<string name="status_disconnected">Disconnected</string>
<string name="status_failed">Connection failed</string>
<string name="status_inactive">Inactive</string>

<!-- Adjust Pane -->
<string name="label_power">Power</string>
<string name="label_brightness">Brightness: %1$d</string>
<string name="label_white_channel">White Channel: %1$d</string>
<string name="label_preset">Preset</string>
<string name="label_classic">Classic</string>

<!-- Modes Pane -->
<string name="title_modes">Modes</string>
<string name="label_speed">Animation Speed</string>
<string name="param_rainbow_delay">Rainbow Delay: %1$dms</string>
<string name="param_spectral_step">Spectral Step Time: %1$dms</string>
<string name="param_fading_hold">Hold Time: %1$dms</string>
<string name="param_fading_fade">Fade Time: %1$dms</string>
<string name="param_flash_on">On Time: %1$dms</string>
<string name="param_flash_off">Off Time: %1$dms</string>
<string name="label_segment">Segment %1$d</string>
<string name="btn_change_color">Change Color</string>

<!-- Timer Pane -->
<string name="title_timer">Timer Settings</string>
<string name="label_enable_timer">Enable Timer</string>
<string name="label_interval_1">Interval 1</string>
<string name="label_interval_2">Interval 2</string>
<string name="label_time_range">From: %1$s To: %2$s</string>
<string name="btn_edit_range">Edit Range</string>
<string name="dialog_timer_title">Set Time Range for %1$s</string>
<string name="label_start_time">Start Time</string>
<string name="label_end_time">End Time</string>
<string name="btn_confirm">Confirm</string>
<string name="btn_cancel">Cancel</string>

<!-- Devices Pane -->
<string name="title_devices">Manage Devices</string>
<string name="status_scanner">Scanner Status: %1$s</string>
<string name="status_searching">Searching for LED strips…</string>
<string name="btn_scan">Scan for Devices</string>
<string name="btn_stop_scan">Stop Scanning</string>
<string name="label_add_manual">Add Manual Device</string>
<string name="label_name">Name</string>
<string name="label_ip_address">IP Address</string>
<string name="placeholder_ip">e.g. 192.168.1.100 (Port 81 is default)</string>
<string name="btn_add">Add</string>
<string name="title_discovered">Discovered Devices</string>
<string name="title_saved">Saved Devices</string>
<string name="label_device_name">Device Name</string>

<!-- Style Pane -->
<string name="title_style">Style</string>
<string name="label_theme_mode">Theme Mode</string>
<string name="theme_system">System</string>
<string name="theme_light">Light</string>
<string name="theme_dark">Dark</string>
<string name="label_dynamic_color">Dynamic Color</string>
<string name="desc_dynamic_color">Use wallpaper colors (Android 12+)</string>

<!-- Scan Statuses -->
<string name="scan_idle">Idle</string>
<string name="scan_scanning">Scanning…</string>
<string name="scan_found">Found: %1$s</string>
<string name="scan_error">Discovery error</string>
<string name="scan_failed">Failed to start</string>
<!-- Modes -->
<string name="mode_single_color">Single color</string>
<string name="mode_rainbow">Rainbow</string>
<string name="mode_spectral">Spectral</string>
<string name="mode_fading">Fading</string>
<string name="mode_flash">Flash</string>
<string name="mode_segments">Segments</string>
</resources>

Cargando…
Cancelar
Guardar