feat(login): Trae/WorkBuddy INTL 提供手机浏览器登录并自动回填;Trae CN 保留软件内登录

This commit is contained in:
Liuxinyu176 2026-10-10 01:17:56 +08:00
parent dab9b7b272
commit 525b91f370
7 changed files with 69 additions and 27 deletions

View File

@ -16,8 +16,8 @@ android {
applicationId = "com.rainy.token" applicationId = "com.rainy.token"
minSdk = 31 minSdk = 31
targetSdk = 35 targetSdk = 35
versionCode = 41 versionCode = 42
versionName = "1.7.21" versionName = "1.7.22"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables { vectorDrawables {

View File

@ -319,6 +319,7 @@ fun ServiceDetailScreen(
hasCredential = uiState.hasCredential, hasCredential = uiState.hasCredential,
isManualMode = isManualMode, isManualMode = isManualMode,
service = service, service = service,
region = uiState.region,
triggerState = triggerState, triggerState = triggerState,
models = models, models = models,
selectedModel = selectedModel, selectedModel = selectedModel,
@ -2105,6 +2106,7 @@ private fun ActionButtons(
hasCredential: Boolean, hasCredential: Boolean,
isManualMode: Boolean, isManualMode: Boolean,
service: ServiceType, service: ServiceType,
region: String?,
triggerState: TriggerState, triggerState: TriggerState,
models: List<String>, models: List<String>,
selectedModel: String?, selectedModel: String?,
@ -2225,6 +2227,18 @@ private fun ActionButtons(
Spacer(modifier = Modifier.height(4.dp)) Spacer(modifier = Modifier.height(4.dp))
} }
// Trae/WorkBuddy 国际版:直接应用内登录(回调解析凭据)
if ((service == ServiceType.TRAE || service == ServiceType.WORKBUDDY) &&
region != null && region.equals("INTL", ignoreCase = true)
) {
OutlinedButton(
onClick = onStartWebViewLogin,
modifier = Modifier.fillMaxWidth()
) {
Text(stringResource(R.string.action_mobile_browser_login))
}
}
if (hasCredential && !isManualMode) { if (hasCredential && !isManualMode) {
Button( Button(
onClick = onRefresh, onClick = onRefresh,

View File

@ -816,27 +816,37 @@ private fun TraeCredentialForm(
style = MaterialTheme.typography.bodySmall, style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.outline color = MaterialTheme.colorScheme.outline
) )
OutlinedButton( if (region.uppercase() != "INTL") {
onClick = onStartInAppLogin, OutlinedButton(
modifier = Modifier.fillMaxWidth() onClick = onStartInAppLogin,
) { modifier = Modifier.fillMaxWidth()
Text(stringResource(R.string.action_login_in_app)) ) {
Text(stringResource(R.string.action_login_in_app))
}
} }
OutlinedTextField( if (region.uppercase() == "INTL") {
value = callbackUrl, OutlinedButton(
onValueChange = onCallbackUrlChange, onClick = onStartInAppLogin,
label = { Text(stringResource(R.string.field_trae_callback_url)) }, modifier = Modifier.fillMaxWidth()
supportingText = { Text(stringResource(R.string.field_trae_callback_hint)) }, ) {
minLines = 1, Text(stringResource(R.string.action_mobile_browser_login))
maxLines = 3, }
modifier = Modifier.fillMaxWidth() OutlinedTextField(
) value = callbackUrl,
OutlinedButton( onValueChange = onCallbackUrlChange,
onClick = { onImportCallback(callbackUrl) }, label = { Text(stringResource(R.string.field_trae_callback_url)) },
enabled = callbackUrl.isNotBlank(), supportingText = { Text(stringResource(R.string.field_trae_callback_hint)) },
modifier = Modifier.fillMaxWidth() minLines = 1,
) { maxLines = 3,
Text(stringResource(R.string.action_parse_callback)) modifier = Modifier.fillMaxWidth()
)
OutlinedButton(
onClick = { onImportCallback(callbackUrl) },
enabled = callbackUrl.isNotBlank(),
modifier = Modifier.fillMaxWidth()
) {
Text(stringResource(R.string.action_parse_callback))
}
} }
OutlinedTextField( OutlinedTextField(
value = jwt, value = jwt,
@ -976,11 +986,13 @@ private fun WorkBuddyCredentialForm(
style = MaterialTheme.typography.bodySmall, style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.outline color = MaterialTheme.colorScheme.outline
) )
OutlinedButton( if (region.uppercase() == "INTL") {
onClick = onStartInAppLogin, OutlinedButton(
modifier = Modifier.fillMaxWidth() onClick = onStartInAppLogin,
) { modifier = Modifier.fillMaxWidth()
Text(stringResource(R.string.action_login_in_app)) ) {
Text(stringResource(R.string.action_mobile_browser_login))
}
} }
Text( Text(
text = stringResource(R.string.field_trae_region), text = stringResource(R.string.field_trae_region),

View File

@ -37,6 +37,7 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.key import androidx.compose.runtime.key
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
@ -77,6 +78,18 @@ fun WebViewLoginScreen(
val uiState by viewModel.uiState.collectAsStateWithLifecycle() val uiState by viewModel.uiState.collectAsStateWithLifecycle()
val snackbarHostState = remember { SnackbarHostState() } val snackbarHostState = remember { SnackbarHostState() }
val context = LocalContext.current val context = LocalContext.current
// Trae/WorkBuddy 国际版:进入即用手机浏览器登录,回调/轮询结果自动回填到应用
val browserLoginService = service == ServiceType.TRAE || service == ServiceType.WORKBUDDY
val browserLoginRegion = region != null && region.equals("INTL", ignoreCase = true)
var autoBrowserStarted by rememberSaveable { mutableStateOf(false) }
LaunchedEffect(uiState.loginUrl, uiState.externalBrowserWaiting, uiState.loginSucceeded, autoBrowserStarted) {
if (browserLoginService && browserLoginRegion && !autoBrowserStarted &&
uiState.loginUrl.isNotBlank() && !uiState.externalBrowserWaiting && !uiState.loginSucceeded
) {
autoBrowserStarted = true
viewModel.openExternalBrowser()
}
}
var webViewGeneration by remember { mutableStateOf(0) } var webViewGeneration by remember { mutableStateOf(0) }
var pageLoading by remember { mutableStateOf(true) } var pageLoading by remember { mutableStateOf(true) }
var pageError by remember { mutableStateOf<String?>(null) } var pageError by remember { mutableStateOf<String?>(null) }

View File

@ -19,6 +19,7 @@
<string name="action_clear">清除</string> <string name="action_clear">清除</string>
<string name="action_save">保存</string> <string name="action_save">保存</string>
<string name="action_login_in_app">软件内登录</string> <string name="action_login_in_app">软件内登录</string>
<string name="action_mobile_browser_login">手机浏览器登录</string>
<string name="action_jump">跳转</string> <string name="action_jump">跳转</string>
<string name="action_prev_page">◀ 上一页</string> <string name="action_prev_page">◀ 上一页</string>
<string name="action_next_page">下一页 ▶</string> <string name="action_next_page">下一页 ▶</string>

View File

@ -19,6 +19,7 @@
<string name="action_clear">清除</string> <string name="action_clear">清除</string>
<string name="action_save">儲存</string> <string name="action_save">儲存</string>
<string name="action_login_in_app">軟體內登入</string> <string name="action_login_in_app">軟體內登入</string>
<string name="action_mobile_browser_login">手機瀏覽器登入</string>
<string name="action_jump">跳轉</string> <string name="action_jump">跳轉</string>
<string name="action_prev_page">◀ 上一頁</string> <string name="action_prev_page">◀ 上一頁</string>
<string name="action_next_page">下一頁 ▶</string> <string name="action_next_page">下一頁 ▶</string>

View File

@ -23,6 +23,7 @@
<string name="action_clear">Clear</string> <string name="action_clear">Clear</string>
<string name="action_save">Save</string> <string name="action_save">Save</string>
<string name="action_login_in_app">Sign in within app</string> <string name="action_login_in_app">Sign in within app</string>
<string name="action_mobile_browser_login">Sign in via mobile browser</string>
<string name="action_jump">Go</string> <string name="action_jump">Go</string>
<string name="action_prev_page">◀ Prev</string> <string name="action_prev_page">◀ Prev</string>
<string name="action_next_page">Next ▶</string> <string name="action_next_page">Next ▶</string>