package com.vgmlr.wedge
import android.content.Context
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.selection.selectable
import androidx.compose.foundation.selection.selectableGroup
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.core.content.edit
import java.net.Inet4Address
import java.net.NetworkInterface
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun SyncViewContent(vm: MainViewModel, prefs: PreferenceManager) {
val context = LocalContext.current
val sharedPrefs = remember { context.getSharedPreferences("sync_prefs", Context.MODE_PRIVATE) }
val editorTxt by prefs.textColor.collectAsState(WedgeConfig.TEXT_COLOR_DEFAULT)
var selectedRoute by remember { mutableStateOf(sharedPrefs.getString("route", "USB") ?: "USB") }
var selectedRole by remember { mutableStateOf(sharedPrefs.getString("role", "Server") ?: "Server") }
val passKeyFlow by prefs.passKey.collectAsState(initial = "wedge03569")
var localPassKey by remember { mutableStateOf<String?>(null) }
val displayPassKey = localPassKey ?: passKeyFlow
val syncStatusText by vm.syncStatus.collectAsState()
val isServerRunning by vm.isServerRunning.collectAsState()
val localIpAddress = remember {
try {
NetworkInterface.getNetworkInterfaces().asSequence()
.flatMap { it.inetAddresses.asSequence() }
.firstOrNull { !it.isLoopbackAddress && it is Inet4Address }
?.hostAddress ?: "Unknown"
} catch (_: Exception) {
"Unknown"
}
}
LaunchedEffect(selectedRoute) {
if (selectedRoute == "USB") {
selectedRole = "Server"
}
}
LaunchedEffect(selectedRoute) { sharedPrefs.edit { putString("route", selectedRoute) } }
LaunchedEffect(selectedRole) { sharedPrefs.edit { putString("role", selectedRole) } }
LaunchedEffect(selectedRoute) {
vm.stopUsbServer()
}
val btnShape = RoundedCornerShape(6.dp)
val labelColor = parseColor(editorTxt).copy(alpha = 0.6f)
val fieldColor = OutlinedTextFieldDefaults.colors(
focusedTextColor = parseColor(editorTxt),
unfocusedTextColor = parseColor(editorTxt),
focusedBorderColor = parseColor(editorTxt).copy(alpha = 0.5f),
unfocusedBorderColor = parseColor(editorTxt).copy(alpha = 0.2f)
)
val btnColors = ButtonDefaults.buttonColors(
containerColor = Color.Gray,
contentColor = Color.Black
)
Column(modifier = Modifier.fillMaxWidth()) {
Spacer(Modifier.height(15.dp))
Row(
modifier = Modifier.fillMaxWidth().padding(vertical = 4.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Start
) {
Box(modifier = Modifier.width(90.dp)) {
SyncLabel("Route", labelColor)
}
SyncRadioGroup(
options = listOf("USB", "WIFI", "Bluetooth"),
selectedOption = selectedRoute,
onOptionSelected = { selectedRoute = it },
textColor = parseColor(editorTxt),
disabledOptions = listOf("Bluetooth")
)
}
Spacer(Modifier.height(14.dp))
Row(
modifier = Modifier.fillMaxWidth().padding(vertical = 4.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Start
) {
Box(modifier = Modifier.width(90.dp)) {
SyncLabel("Role", labelColor)
}
SyncRadioGroup(
options = listOf("Client", "Server"),
selectedOption = selectedRole,
onOptionSelected = { selectedRole = it },
textColor = parseColor(editorTxt),
disabledOptions = listOf("Client")
)
}
Spacer(Modifier.height(14.dp))
Row(
modifier = Modifier.fillMaxWidth().padding(vertical = 4.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Start
) {
Box(modifier = Modifier.width(90.dp)) {
SyncLabel("Local IP", labelColor)
}
Text(
text = localIpAddress,
color = parseColor(editorTxt),
fontSize = 15.sp
)
}
Spacer(Modifier.height(14.dp))
Row(
modifier = Modifier.fillMaxWidth().padding(vertical = 4.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Start
) {
Box(modifier = Modifier.width(90.dp)) {
SyncLabel("Pass Key", labelColor)
}
OutlinedTextField(
value = displayPassKey,
onValueChange = {
localPassKey = it
vm.updatePassKey(it)
},
modifier = Modifier.fillMaxWidth(),
colors = fieldColor
)
}
Spacer(Modifier.height(14.dp))
Row(
modifier = Modifier.fillMaxWidth().padding(vertical = 4.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Start
) {
Box(modifier = Modifier.width(90.dp)) {
SyncLabel("Status", labelColor)
}
Text(
text = syncStatusText,
color = parseColor(editorTxt),
fontSize = 15.sp
)
}
Spacer(Modifier.height(20.dp))
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(16.dp)
) {
Button(
onClick = {
if (isServerRunning) {
vm.stopUsbServer()
} else {
if (selectedRoute == "USB" || selectedRoute == "WIFI") {
vm.startUsbServer()
}
}
},
modifier = Modifier.weight(1f).height(48.dp),
colors = btnColors,
shape = btnShape
) {
Text(
text = if (isServerRunning) "Disconnect" else "Connect",
color = Color.Black,
fontSize = 16.sp
)
}
Button(
onClick = { vm.sendNoteText() },
modifier = Modifier.weight(1f).height(48.dp),
colors = btnColors,
shape = btnShape
) {
Text("Send", color = Color.Black, fontSize = 16.sp)
}
}
}
}
@Composable
private fun SyncLabel(text: String, color: Color) {
Text(
text = text,
color = color,
fontWeight = FontWeight.Bold,
fontSize = 16.sp,
)
}
@Composable
private fun SyncRadioGroup(
options: List<String>,
selectedOption: String,
onOptionSelected: (String) -> Unit,
textColor: Color,
disabledOptions: List<String> = emptyList()
) {
Row(
modifier = Modifier
.fillMaxWidth()
.selectableGroup(),
horizontalArrangement = Arrangement.Start,
verticalAlignment = Alignment.CenterVertically
) {
options.forEachIndexed { index, text ->
val interactionSource = remember { MutableInteractionSource() }
val isEnabled = text !in disabledOptions
Row(
modifier = Modifier
.selectable(
selected = (text == selectedOption),
onClick = { onOptionSelected(text) },
role = Role.RadioButton,
interactionSource = interactionSource,
indication = null,
enabled = isEnabled
)
.padding(vertical = 4.dp),
verticalAlignment = Alignment.CenterVertically
) {
RadioButton(
selected = (text == selectedOption),
onClick = null,
enabled = isEnabled,
colors = RadioButtonDefaults.colors(
selectedColor = colorResource(id = R.color.dialog_color).copy(alpha = 0.8f),
unselectedColor = colorResource(id = R.color.dialog_color).copy(alpha = 0.6f),
disabledSelectedColor = colorResource(id = R.color.dialog_color).copy(alpha = 0.3f),
disabledUnselectedColor = colorResource(id = R.color.dialog_color).copy(alpha = 0.2f)
)
)
Text(
text = text,
color = if (isEnabled) textColor else textColor.copy(alpha = 0.5f),
fontSize = 15.sp,
modifier = Modifier.padding(start = 4.dp)
)
}
if (index < options.lastIndex) {
Spacer(Modifier.width(16.dp))
}
}
}
}