fix: resolve auto-connect race conditions and pairing success redirects

This commit is contained in:
2026-07-04 14:50:11 -06:00
parent cffbeb0e4c
commit e4048fd78c
3 changed files with 33 additions and 13 deletions

View File

@@ -125,8 +125,8 @@ fun Application.module() {
} }
} }
// Send pairing success indicator screen only if we actually paired in this session // Send pairing success indicator screen only if we actually paired in this session AND no specific profile was requested
if (showedPairingScreen) { if (showedPairingScreen && profileId == null) {
val successMessage = Message( val successMessage = Message(
id = "pairing_success_$deviceId", id = "pairing_success_$deviceId",
title = "Pairing Successful", title = "Pairing Successful",

View File

@@ -95,6 +95,11 @@ class MainActivity : ComponentActivity() {
// SharedPreferences for connection details // SharedPreferences for connection details
val prefs = getSharedPreferences("meatbag_prefs", Context.MODE_PRIVATE) val prefs = getSharedPreferences("meatbag_prefs", Context.MODE_PRIVATE)
val deviceId = prefs.getString("device_id", null) ?: run {
val newId = java.util.UUID.randomUUID().toString()
prefs.edit().putString("device_id", newId).apply()
newId
}
setContent { setContent {
var host by remember { mutableStateOf(prefs.getString("host", "10.0.0.7") ?: "10.0.0.7") } var host by remember { mutableStateOf(prefs.getString("host", "10.0.0.7") ?: "10.0.0.7") }
@@ -110,18 +115,21 @@ class MainActivity : ComponentActivity() {
var errorMessage by remember { mutableStateOf<String?>(null) } var errorMessage by remember { mutableStateOf<String?>(null) }
var activeProfileId by remember { mutableStateOf(intent.getStringExtra("LAUNCH_PROFILE_ID")) } var activeProfileId by remember { mutableStateOf(intent.getStringExtra("LAUNCH_PROFILE_ID")) }
var isInitialLaunch by remember { mutableStateOf(true) }
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
onProfileLaunch = { id -> onProfileLaunch = { id ->
activeProfileId = id activeProfileId = id
isInitialLaunch = true
} }
} }
// Auto-connect when a specific profile is launched via shortcut or intent // Auto-connect when a specific profile is launched via shortcut or intent on startup/new intent
LaunchedEffect(activeProfileId) { LaunchedEffect(activeProfileId) {
if (activeProfileId != null && connectionState == ConnectionState.DISCONNECTED && clientState == null) { if (isInitialLaunch && activeProfileId != null && connectionState == ConnectionState.DISCONNECTED && clientState == null) {
isInitialLaunch = false
errorMessage = null errorMessage = null
scope.launch(Dispatchers.Default) { scope.launch(Dispatchers.Default) {
val newClient = MeatbagClient(host.trim(), portStr.trim().toIntOrNull() ?: 8080, feedback) val newClient = MeatbagClient(host.trim(), portStr.trim().toIntOrNull() ?: 8080, feedback, deviceId = deviceId)
clientState = newClient clientState = newClient
newClient.connect(deviceType, activeProfileId) newClient.connect(deviceType, activeProfileId)
} }
@@ -380,6 +388,7 @@ class MainActivity : ComponentActivity() {
Button( Button(
onClick = { onClick = {
isInitialLaunch = false
errorMessage = null errorMessage = null
// Save preferences // Save preferences
prefs.edit().apply { prefs.edit().apply {
@@ -405,7 +414,8 @@ class MainActivity : ComponentActivity() {
host = host.trim(), host = host.trim(),
port = portStr.trim().toIntOrNull() ?: 8080, port = portStr.trim().toIntOrNull() ?: 8080,
attentionFeedback = feedback, attentionFeedback = feedback,
socksPort = socksPort socksPort = socksPort,
deviceId = deviceId
) )
clientState = newClient clientState = newClient
newClient.connect(deviceType, activeProfileId) newClient.connect(deviceType, activeProfileId)
@@ -471,9 +481,10 @@ class MainActivity : ComponentActivity() {
Button( Button(
onClick = { onClick = {
activeProfileId = "meatbag" activeProfileId = "meatbag"
isInitialLaunch = false
errorMessage = null errorMessage = null
scope.launch(Dispatchers.Default) { scope.launch(Dispatchers.Default) {
val newClient = MeatbagClient(host.trim(), portStr.trim().toIntOrNull() ?: 8080, feedback) val newClient = MeatbagClient(host.trim(), portStr.trim().toIntOrNull() ?: 8080, feedback, deviceId = deviceId)
clientState = newClient clientState = newClient
newClient.connect(deviceType, "meatbag") newClient.connect(deviceType, "meatbag")
} }
@@ -513,9 +524,10 @@ class MainActivity : ComponentActivity() {
Button( Button(
onClick = { onClick = {
activeProfileId = "lm_studio_remote" activeProfileId = "lm_studio_remote"
isInitialLaunch = false
errorMessage = null errorMessage = null
scope.launch(Dispatchers.Default) { scope.launch(Dispatchers.Default) {
val newClient = MeatbagClient(host.trim(), portStr.trim().toIntOrNull() ?: 8080, feedback) val newClient = MeatbagClient(host.trim(), portStr.trim().toIntOrNull() ?: 8080, feedback, deviceId = deviceId)
clientState = newClient clientState = newClient
newClient.connect(deviceType, "lm_studio_remote") newClient.connect(deviceType, "lm_studio_remote")
} }

View File

@@ -35,7 +35,8 @@ class MeatbagClient(
private val host: String = "localhost", private val host: String = "localhost",
private val port: Int = 8080, private val port: Int = 8080,
private val attentionFeedback: AttentionFeedback = NoOpAttentionFeedback, private val attentionFeedback: AttentionFeedback = NoOpAttentionFeedback,
private val socksPort: Int? = null private val socksPort: Int? = null,
private val deviceId: String? = null
) { ) {
private val jsonConfig = Json { private val jsonConfig = Json {
ignoreUnknownKeys = true ignoreUnknownKeys = true
@@ -76,10 +77,17 @@ class MeatbagClient(
connectionJob = CoroutineScope(Dispatchers.Default).launch { connectionJob = CoroutineScope(Dispatchers.Default).launch {
while (isActive) { while (isActive) {
try { try {
val queryParams = if (profileId != null) { val queryParams = buildString {
"?deviceType=$deviceType&profileId=$profileId" append("?deviceType=")
} else { append(deviceType)
"?deviceType=$deviceType" if (profileId != null) {
append("&profileId=")
append(profileId)
}
if (deviceId != null) {
append("&deviceId=")
append(deviceId)
}
} }
log("Connecting to ws://$host:$port/ws/connect$queryParams ...") log("Connecting to ws://$host:$port/ws/connect$queryParams ...")
_connectionState.value = ConnectionState.CONNECTING _connectionState.value = ConnectionState.CONNECTING