tahamajs's picture
download
raw
23.9 kB
import re
# 1. Replace ConnectionControlsCard in HomeScreen.kt
with open('app/src/main/java/com/airmouse/presentation/ui/home/HomeScreen.kt', 'r') as f:
content = f.read()
# Find the start of ConnectionControlsCard
start_idx = content.find('@Composable\nfun ConnectionControlsCard(')
# Find the end of the function (counting braces)
if start_idx != -1:
brace_count = 0
in_function = False
end_idx = -1
for i in range(start_idx, len(content)):
if content[i] == '{':
brace_count += 1
in_function = True
elif content[i] == '}':
brace_count -= 1
if in_function and brace_count == 0:
end_idx = i + 1
break
if end_idx != -1:
new_card = """@Composable
fun ConnectionControlsCard(
ip: String,
port: Int,
protocol: ConnectionProtocol,
onIpChange: (String) -> Unit,
onPortChange: (Int) -> Unit,
onProtocolChange: (ConnectionProtocol) -> Unit,
onConnect: () -> Unit,
onScanQr: () -> Unit,
isConnecting: Boolean,
isConnected: Boolean = false,
isRegistered: Boolean,
lastServer: String
) {
val focusManager = LocalFocusManager.current
val infiniteTransition = rememberInfiniteTransition(label = "cardGradient")
val gradientOffset by infiniteTransition.animateFloat(
initialValue = 0f,
targetValue = 1000f,
animationSpec = infiniteRepeatable(
animation = tween(15000, easing = LinearEasing),
repeatMode = RepeatMode.Reverse
),
label = "gradientOffset"
)
Card(
modifier = Modifier.fillMaxWidth().graphicsLayer {
shadowElevation = 16.dp.toPx()
shape = RoundedCornerShape(28.dp)
clip = true
},
shape = RoundedCornerShape(28.dp),
colors = CardDefaults.cardColors(containerColor = Color.Transparent)
) {
Box(
modifier = Modifier
.fillMaxWidth()
.background(
Brush.linearGradient(
colors = listOf(
MaterialTheme.colorScheme.surface,
MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.8f),
MaterialTheme.colorScheme.surface
),
start = androidx.compose.ui.geometry.Offset(gradientOffset, 0f),
end = androidx.compose.ui.geometry.Offset(gradientOffset + 500f, 1000f)
)
)
.padding(24.dp)
) {
Column(verticalArrangement = Arrangement.spacedBy(16.dp)) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Column {
Text(
text = if (protocol == ConnectionProtocol.BLUETOOTH) "Bluetooth Mouse" else "Connect Server",
style = MaterialTheme.typography.headlineSmall,
fontWeight = FontWeight.ExtraBold,
color = MaterialTheme.colorScheme.primary
)
if (lastServer.isNotEmpty()) {
Text(
text = "Last used: $lastServer",
style = MaterialTheme.typography.labelMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
}
}
Surface(
shape = CircleShape,
color = MaterialTheme.colorScheme.primaryContainer,
modifier = Modifier.size(48.dp)
) {
Icon(
imageVector = if (protocol == ConnectionProtocol.BLUETOOTH) Icons.Default.Bluetooth else Icons.Default.Computer,
contentDescription = null,
tint = MaterialTheme.colorScheme.onPrimaryContainer,
modifier = Modifier.padding(12.dp)
)
}
}
if (!isRegistered) {
Surface(
color = MaterialTheme.colorScheme.errorContainer,
shape = RoundedCornerShape(12.dp),
modifier = Modifier.fillMaxWidth()
) {
Row(modifier = Modifier.padding(12.dp), verticalAlignment = Alignment.CenterVertically) {
Icon(Icons.Default.ErrorOutline, contentDescription = null, tint = MaterialTheme.colorScheme.onErrorContainer)
Spacer(modifier = Modifier.width(8.dp))
Text("Waiting for profile registration", color = MaterialTheme.colorScheme.onErrorContainer, style = MaterialTheme.typography.labelMedium)
}
}
}
OutlinedTextField(
value = ip,
onValueChange = onIpChange,
label = { Text(if (protocol == ConnectionProtocol.BLUETOOTH) "MAC Address" else "IP Address") },
placeholder = { Text(if (protocol == ConnectionProtocol.BLUETOOTH) "00:11:22:33:44:55 (optional)" else "192.168.1.100") },
leadingIcon = {
Icon(
if (protocol == ConnectionProtocol.BLUETOOTH) Icons.Default.Bluetooth else Icons.Default.Wifi,
contentDescription = null,
tint = MaterialTheme.colorScheme.primary
)
},
modifier = Modifier.fillMaxWidth(),
singleLine = true,
enabled = !isConnecting && !isConnected,
shape = RoundedCornerShape(20.dp),
colors = OutlinedTextFieldDefaults.colors(
focusedBorderColor = MaterialTheme.colorScheme.primary,
unfocusedBorderColor = MaterialTheme.colorScheme.outline.copy(alpha = 0.5f),
focusedContainerColor = MaterialTheme.colorScheme.surface,
unfocusedContainerColor = MaterialTheme.colorScheme.surface
),
keyboardOptions = KeyboardOptions(
imeAction = if (protocol == ConnectionProtocol.BLUETOOTH || protocol == ConnectionProtocol.USB_SERIAL) ImeAction.Done else ImeAction.Next
),
keyboardActions = KeyboardActions(
onDone = {
focusManager.clearFocus()
if (!isConnecting && !isConnected) onConnect()
}
)
)
AnimatedVisibility(
visible = protocol != ConnectionProtocol.BLUETOOTH && protocol != ConnectionProtocol.USB_SERIAL,
enter = expandVertically() + fadeIn(),
exit = shrinkVertically() + fadeOut()
) {
OutlinedTextField(
value = port.toString(),
onValueChange = { it.toIntOrNull()?.let(onPortChange) },
label = { Text("Port") },
placeholder = { Text("8082") },
leadingIcon = {
Icon(
Icons.Default.SettingsEthernet,
contentDescription = null,
tint = MaterialTheme.colorScheme.primary
)
},
modifier = Modifier.fillMaxWidth(),
singleLine = true,
keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.Number,
imeAction = ImeAction.Done
),
keyboardActions = KeyboardActions(
onDone = {
focusManager.clearFocus()
if (!isConnecting && !isConnected) onConnect()
}
),
enabled = !isConnecting && !isConnected,
shape = RoundedCornerShape(20.dp),
colors = OutlinedTextFieldDefaults.colors(
focusedBorderColor = MaterialTheme.colorScheme.primary,
unfocusedBorderColor = MaterialTheme.colorScheme.outline.copy(alpha = 0.5f),
focusedContainerColor = MaterialTheme.colorScheme.surface,
unfocusedContainerColor = MaterialTheme.colorScheme.surface
)
)
}
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
Text(
"Protocol",
style = MaterialTheme.typography.labelMedium,
fontWeight = FontWeight.Bold,
color = MaterialTheme.colorScheme.primary
)
Row(
modifier = Modifier.horizontalScroll(rememberScrollState()),
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
listOf(
ConnectionProtocol.UDP to "UDP",
ConnectionProtocol.WEBSOCKET to "WebSocket",
ConnectionProtocol.TCP to "TCP",
ConnectionProtocol.USB_SERIAL to "USB"
).forEach { (prot, label) ->
val isSelected = protocol == prot
Surface(
onClick = { if (!isConnected) onProtocolChange(prot) },
shape = RoundedCornerShape(16.dp),
color = if (isSelected) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.5f),
border = if (isSelected) null else BorderStroke(1.dp, MaterialTheme.colorScheme.outline.copy(alpha = 0.2f)),
modifier = Modifier.animateContentSize()
) {
Row(
modifier = Modifier.padding(horizontal = 16.dp, vertical = 10.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(6.dp)
) {
if (isSelected) {
Icon(Icons.Default.CheckCircle, contentDescription = null, modifier = Modifier.size(16.dp), tint = MaterialTheme.colorScheme.onPrimary)
}
Text(
text = label,
fontWeight = if (isSelected) FontWeight.Bold else FontWeight.Medium,
color = if (isSelected) MaterialTheme.colorScheme.onPrimary else MaterialTheme.colorScheme.onSurface
)
}
}
}
}
Text(
text = when (protocol) {
ConnectionProtocol.UDP -> "Ultra-low latency for direct local control."
ConnectionProtocol.WEBSOCKET -> "Reliable control with web-based servers."
ConnectionProtocol.TCP -> "Direct socket connection for custom setups."
ConnectionProtocol.USB_SERIAL -> "Zero-latency wired connection."
ConnectionProtocol.BLUETOOTH -> "Native wireless mouse emulation."
},
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
}
Spacer(modifier = Modifier.height(8.dp))
Button(
onClick = onConnect,
enabled = !isConnecting && isRegistered && !isConnected,
modifier = Modifier
.fillMaxWidth()
.height(64.dp),
shape = RoundedCornerShape(24.dp),
colors = ButtonDefaults.buttonColors(
containerColor = MaterialTheme.colorScheme.primary,
contentColor = MaterialTheme.colorScheme.onPrimary
),
elevation = ButtonDefaults.buttonElevation(
defaultElevation = 8.dp,
pressedElevation = 2.dp
)
) {
if (isConnecting) {
SweepGradientGlowRing(
modifier = Modifier.size(24.dp),
strokeWidth = 2.5.dp,
glowRadius = 4.dp,
colors = listOf(
MaterialTheme.colorScheme.onPrimary,
MaterialTheme.colorScheme.onPrimary.copy(alpha = 0.3f),
MaterialTheme.colorScheme.onPrimary
)
)
Spacer(modifier = Modifier.width(12.dp))
Text("Connecting...", fontWeight = FontWeight.Bold, fontSize = 18.sp)
} else if (isConnected) {
Icon(Icons.Default.Check, contentDescription = null)
Spacer(modifier = Modifier.width(8.dp))
Text("Connected", fontWeight = FontWeight.Bold, fontSize = 18.sp)
} else {
Icon(Icons.Default.RocketLaunch, contentDescription = null)
Spacer(modifier = Modifier.width(12.dp))
Text(if (protocol == ConnectionProtocol.BLUETOOTH) "Broadcast" else "Launch Connection", fontWeight = FontWeight.Bold, fontSize = 18.sp)
}
}
if (protocol != ConnectionProtocol.BLUETOOTH) {
OutlinedButton(
onClick = onScanQr,
modifier = Modifier
.fillMaxWidth()
.height(56.dp),
shape = RoundedCornerShape(24.dp),
colors = ButtonDefaults.outlinedButtonColors(
contentColor = MaterialTheme.colorScheme.primary
),
border = BorderStroke(1.5.dp, MaterialTheme.colorScheme.primary.copy(alpha = 0.5f)),
enabled = !isConnecting && isRegistered && !isConnected
) {
Icon(Icons.Default.QrCodeScanner, contentDescription = null)
Spacer(modifier = Modifier.width(8.dp))
Text("Quick Scan QR", fontWeight = FontWeight.Bold, fontSize = 16.sp)
}
}
}
}
}
}"""
new_content = content[:start_idx] + new_card + content[end_idx:]
with open('app/src/main/java/com/airmouse/presentation/ui/home/HomeScreen.kt', 'w') as f:
f.write(new_content)
print("Updated HomeScreen.kt")
# 2. Replace ManualConnectDialog in NetworkDiscoveryScreen.kt
with open('app/src/main/java/com/airmouse/presentation/ui/network/NetworkDiscoveryScreen.kt', 'r') as f:
content = f.read()
start_idx = content.find('@Composable\nfun ManualConnectDialog(')
if start_idx != -1:
brace_count = 0
in_function = False
end_idx = -1
for i in range(start_idx, len(content)):
if content[i] == '{':
brace_count += 1
in_function = True
elif content[i] == '}':
brace_count -= 1
if in_function and brace_count == 0:
end_idx = i + 1
break
if end_idx != -1:
new_dialog = """@Composable
fun ManualConnectDialog(
onDismiss: () -> Unit,
onConnect: (String, Int) -> Unit,
initialIp: String,
initialPort: Int
) {
var ip by remember { mutableStateOf(initialIp) }
var port by remember { mutableStateOf(initialPort.toString()) }
var isValidIp by remember { mutableStateOf(true) }
val portInt = port.toIntOrNull()
val isValidPort = portInt != null && portInt in 1..65535
androidx.compose.ui.window.Dialog(onDismissRequest = onDismiss) {
Surface(
shape = RoundedCornerShape(28.dp),
color = MaterialTheme.colorScheme.surface,
tonalElevation = 8.dp,
shadowElevation = 24.dp,
modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp)
) {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(24.dp),
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(12.dp)) {
Surface(
shape = CircleShape,
color = MaterialTheme.colorScheme.primaryContainer,
modifier = Modifier.size(48.dp)
) {
Icon(
Icons.Default.AddLink,
contentDescription = null,
tint = MaterialTheme.colorScheme.onPrimaryContainer,
modifier = Modifier.padding(12.dp)
)
}
Column {
Text(
"Manual Connect",
style = MaterialTheme.typography.titleLarge,
fontWeight = FontWeight.ExtraBold,
color = MaterialTheme.colorScheme.onSurface
)
Text(
"Direct server IP entry",
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
}
}
Spacer(modifier = Modifier.height(8.dp))
OutlinedTextField(
value = ip,
onValueChange = {
ip = it
isValidIp = it.matches("^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\\\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$".toRegex()) || it.isEmpty()
},
label = { Text("IP Address") },
placeholder = { Text("192.168.1.100") },
leadingIcon = { Icon(Icons.Default.Wifi, contentDescription = null, tint = MaterialTheme.colorScheme.primary) },
isError = !isValidIp && ip.isNotEmpty(),
singleLine = true,
modifier = Modifier.fillMaxWidth(),
shape = RoundedCornerShape(20.dp),
colors = OutlinedTextFieldDefaults.colors(
focusedBorderColor = MaterialTheme.colorScheme.primary,
unfocusedBorderColor = MaterialTheme.colorScheme.outline.copy(alpha = 0.5f)
)
)
if (!isValidIp && ip.isNotEmpty()) {
Text(
"Please enter a valid IPv4 address",
fontSize = 12.sp,
color = MaterialTheme.colorScheme.error,
modifier = Modifier.padding(start = 16.dp)
)
}
OutlinedTextField(
value = port,
onValueChange = { port = it.filter { c -> c.isDigit() } },
label = { Text("Port") },
leadingIcon = { Icon(Icons.Default.SettingsEthernet, contentDescription = null, tint = MaterialTheme.colorScheme.primary) },
isError = !isValidPort && port.isNotEmpty(),
singleLine = true,
modifier = Modifier.fillMaxWidth(),
shape = RoundedCornerShape(20.dp),
colors = OutlinedTextFieldDefaults.colors(
focusedBorderColor = MaterialTheme.colorScheme.primary,
unfocusedBorderColor = MaterialTheme.colorScheme.outline.copy(alpha = 0.5f)
),
keyboardOptions = androidx.compose.foundation.text.KeyboardOptions(
keyboardType = androidx.compose.ui.text.input.KeyboardType.Number
)
)
if (!isValidPort && port.isNotEmpty()) {
Text(
"Port must be between 1 and 65535",
fontSize = 12.sp,
color = MaterialTheme.colorScheme.error,
modifier = Modifier.padding(start = 16.dp)
)
}
Spacer(modifier = Modifier.height(16.dp))
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(12.dp, Alignment.End)
) {
TextButton(
onClick = onDismiss,
shape = RoundedCornerShape(16.dp)
) {
Text("Cancel", fontWeight = FontWeight.SemiBold)
}
Button(
onClick = {
if (ip.isNotEmpty() && isValidIp && isValidPort && portInt != null) {
onConnect(ip, portInt)
}
},
enabled = isValidIp && ip.isNotEmpty() && isValidPort,
shape = RoundedCornerShape(16.dp),
colors = ButtonDefaults.buttonColors(
containerColor = MaterialTheme.colorScheme.primary,
contentColor = MaterialTheme.colorScheme.onPrimary
)
) {
Icon(Icons.Default.RocketLaunch, contentDescription = null, modifier = Modifier.size(18.dp))
Spacer(modifier = Modifier.width(8.dp))
Text("Connect", fontWeight = FontWeight.Bold)
}
}
}
}
}
}"""
new_content = content[:start_idx] + new_dialog + content[end_idx:]
with open('app/src/main/java/com/airmouse/presentation/ui/network/NetworkDiscoveryScreen.kt', 'w') as f:
f.write(new_content)
print("Updated NetworkDiscoveryScreen.kt")

Xet Storage Details

Size:
23.9 kB
·
Xet hash:
ec4c02f2c9c3b11b2f1533809e35a0f9cd3aeaddeeecbf539bee7074b59b6c4e

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.