fix(android): auto-resume pairing approval

This commit is contained in:
Ayaan Zaidi
2026-04-08 20:20:51 +05:30
parent 253ecd2a5d
commit 911f9a104c
2 changed files with 46 additions and 2 deletions

View File

@@ -38,6 +38,7 @@ import androidx.compose.material3.SwitchDefaults
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -55,12 +56,15 @@ import androidx.compose.ui.unit.dp
import ai.openclaw.app.MainViewModel
import ai.openclaw.app.gateway.GatewayEndpoint
import ai.openclaw.app.ui.mobileCardSurface
import kotlinx.coroutines.delay
private enum class ConnectInputMode {
SetupCode,
Manual,
}
private const val PAIRING_AUTO_RETRY_MS = 6_000L
@Composable
fun ConnectTabScreen(viewModel: MainViewModel) {
val context = LocalContext.current
@@ -140,8 +144,19 @@ fun ConnectTabScreen(viewModel: MainViewModel) {
}
val showDiagnostics = !isConnected && gatewayStatusHasDiagnostics(statusText)
val pairingRequired = !isConnected && gatewayStatusLooksLikePairing(statusText)
val statusLabel = gatewayStatusForDisplay(statusText)
LaunchedEffect(pairingRequired) {
if (!pairingRequired) {
return@LaunchedEffect
}
while (true) {
delay(PAIRING_AUTO_RETRY_MS)
viewModel.refreshGatewayConnection()
}
}
Column(
modifier = Modifier.verticalScroll(rememberScrollState()).padding(horizontal = 20.dp, vertical = 16.dp),
verticalArrangement = Arrangement.spacedBy(14.dp),
@@ -319,8 +334,17 @@ fun ConnectTabScreen(viewModel: MainViewModel) {
modifier = Modifier.fillMaxWidth().padding(horizontal = 14.dp, vertical = 14.dp),
verticalArrangement = Arrangement.spacedBy(10.dp),
) {
Text("Last gateway error", style = mobileHeadline, color = mobileWarning)
Text(if (pairingRequired) "Pairing required" else "Last gateway error", style = mobileHeadline, color = mobileWarning)
Text(statusLabel, style = mobileBody.copy(fontFamily = FontFamily.Monospace), color = mobileText)
if (pairingRequired) {
Text(
"Approve this phone on the gateway. OpenClaw retries automatically while this screen stays open.",
style = mobileCallout,
color = mobileTextSecondary,
)
CommandBlock("openclaw devices list")
CommandBlock("openclaw devices approve <requestId>")
}
Text("OpenClaw Android ${openClawAndroidVersionLabel()}", style = mobileCaption1, color = mobileTextSecondary)
Button(
onClick = {

View File

@@ -71,6 +71,7 @@ import androidx.compose.material.icons.filled.Wifi
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -101,6 +102,7 @@ import ai.openclaw.app.node.DeviceNotificationListenerService
import com.google.mlkit.vision.barcode.common.Barcode
import com.google.mlkit.vision.codescanner.GmsBarcodeScannerOptions
import com.google.mlkit.vision.codescanner.GmsBarcodeScanning
import kotlinx.coroutines.delay
private enum class OnboardingStep(val index: Int, val label: String) {
Welcome(1, "Welcome"),
@@ -132,6 +134,8 @@ private enum class SpecialAccessToggle {
NotificationListener,
}
private const val PAIRING_AUTO_RETRY_MS = 6_000L
private val onboardingBackgroundGradient: Brush
@Composable get() = mobileBackgroundGradient
@@ -737,6 +741,7 @@ fun OnboardingFlow(viewModel: MainViewModel, modifier: Modifier = Modifier) {
)
OnboardingStep.FinalCheck ->
FinalStep(
viewModel = viewModel,
parsedGateway = parseGatewayEndpoint(gatewayUrl),
statusText = statusText,
isConnected = canFinishOnboarding,
@@ -1566,6 +1571,7 @@ private fun PermissionToggleRow(
@Composable
private fun FinalStep(
viewModel: MainViewModel,
parsedGateway: GatewayEndpointConfig?,
statusText: String,
isConnected: Boolean,
@@ -1581,6 +1587,16 @@ private fun FinalStep(
val showDiagnostics = gatewayStatusHasDiagnostics(statusText)
val pairingRequired = gatewayStatusLooksLikePairing(statusText)
LaunchedEffect(pairingRequired, attemptedConnect) {
if (!pairingRequired || !attemptedConnect) {
return@LaunchedEffect
}
while (true) {
delay(PAIRING_AUTO_RETRY_MS)
viewModel.refreshGatewayConnection()
}
}
Column(verticalArrangement = Arrangement.spacedBy(10.dp)) {
Text("Review", style = onboardingTitle1Style, color = onboardingText)
@@ -1761,7 +1777,11 @@ private fun FinalStep(
if (pairingRequired) {
CommandBlock("openclaw devices list")
CommandBlock("openclaw devices approve <requestId>")
Text("Then tap Connect again.", style = onboardingCalloutStyle, color = onboardingTextSecondary)
Text(
"OpenClaw retries automatically while this screen stays open.",
style = onboardingCalloutStyle,
color = onboardingTextSecondary,
)
}
}
}