复用连接

This commit is contained in:
2026-10-28 13:51:20 +08:00
parent 0851713876
commit 5739f7f8e7
15 changed files with 581 additions and 376 deletions
@@ -15,14 +15,12 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import net.bluemangoo.indpjsk.R
import net.bluemangoo.indpjsk.data.CaptureProfile
import net.bluemangoo.indpjsk.data.CaptureProfileStep
import net.bluemangoo.indpjsk.data.SettingsStore
import net.bluemangoo.indpjsk.data.StepSelectionStore
import net.bluemangoo.indpjsk.state.CaptureStatus
import net.bluemangoo.indpjsk.vpn.LogEntry
@@ -36,6 +34,7 @@ fun CaptureScreen(
onStartCapture: () -> Unit,
onStopCapture: () -> Unit,
onOpenProxySettings: () -> Unit,
onOpenUploadSettings: () -> Unit,
onOpenFiles: () -> Unit,
onOpenAbout: () -> Unit,
) {
@@ -46,7 +45,6 @@ fun CaptureScreen(
var enabledMap by remember { mutableStateOf(StepSelectionStore.loadAll(context) as Map<String, Boolean>) }
var menuExpanded by remember { mutableStateOf(false) }
var profileExpanded by remember { mutableStateOf(false) }
var showUploadCodeDialog by remember { mutableStateOf(false) }
val isRunning = state.status == CaptureStatus.RUNNING
val isCaptured = state.status == CaptureStatus.CAPTURED
@@ -69,10 +67,10 @@ fun CaptureScreen(
onDismissRequest = { menuExpanded = false }
) {
DropdownMenuItem(
text = { Text("上传码") },
text = { Text("上传设置") },
onClick = {
menuExpanded = false
showUploadCodeDialog = true
onOpenUploadSettings()
}
)
DropdownMenuItem(
@@ -215,48 +213,6 @@ fun CaptureScreen(
}
}
}
if (showUploadCodeDialog) {
UploadCodeDialog(onDismiss = { showUploadCodeDialog = false })
}
}
@Composable
private fun UploadCodeDialog(onDismiss: () -> Unit) {
val context = LocalContext.current
var text by remember { mutableStateOf(SettingsStore.loadUploadCode(context)) }
AlertDialog(
onDismissRequest = onDismiss,
title = { Text("上传码") },
text = {
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
OutlinedTextField(
value = text,
onValueChange = { text = it.trim() },
modifier = Modifier.fillMaxWidth(),
singleLine = true,
label = { Text("Haruki 上传码") },
visualTransformation = PasswordVisualTransformation(),
textStyle = MaterialTheme.typography.bodyMedium
)
Text(
text = "从 Haruki 工具箱 - 数据上传 - iOS 获取。",
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
}
},
confirmButton = {
TextButton(onClick = {
SettingsStore.saveUploadCode(context, text)
onDismiss()
}) { Text("保存") }
},
dismissButton = {
TextButton(onClick = onDismiss) { Text("取消") }
}
)
}
@Composable
@@ -29,6 +29,7 @@ fun ProxySettingsScreen(
) {
val context = LocalContext.current
var proxy by remember { mutableStateOf(SettingsStore.loadProxy(context)) }
var useProxy by remember { mutableStateOf(SettingsStore.loadUseProxy(context)) }
var pendingRemove by remember { mutableStateOf<AppEntry?>(null) }
val entries = remember(bypassPackages) {
@@ -37,6 +38,7 @@ fun ProxySettingsScreen(
val looksValid = proxy.isBlank() ||
((proxy.startsWith("http://") || proxy.startsWith("https://")) && proxy.contains(":"))
val canUseProxy = proxy.isNotBlank() && looksValid
Scaffold(
topBar = {
@@ -66,11 +68,35 @@ fun ProxySettingsScreen(
) {
Text("上游代理", style = MaterialTheme.typography.labelLarge)
Row(
modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically
) {
Checkbox(
checked = useProxy && canUseProxy,
onCheckedChange = {
useProxy = it
SettingsStore.saveUseProxy(context, it)
},
enabled = canUseProxy
)
Text(
text = "使用上游代理",
style = MaterialTheme.typography.bodyMedium
)
}
OutlinedTextField(
value = proxy,
onValueChange = {
proxy = it
SettingsStore.saveProxy(context, it)
// 地址被清空时不允许再勾着;顺手把偏好也清掉,避免重新填写时自动勾上
if (it.isBlank() && useProxy) {
useProxy = false
SettingsStore.saveUseProxy(context, false)
}
},
modifier = Modifier.fillMaxWidth(),
singleLine = true,
@@ -79,10 +105,10 @@ fun ProxySettingsScreen(
isError = !looksValid,
supportingText = {
Text(
text = if (looksValid) {
"留空以忽略;只支持 http / https 代理"
} else {
"格式示例:http://127.0.0.1:7890"
text = when {
proxy.isBlank() -> "填写地址后才能勾选启用;只支持 http / https 代理"
!looksValid -> "格式示例:http://127.0.0.1:7890"
else -> "只支持 http / https 代理"
}
)
}
@@ -0,0 +1,95 @@
package net.bluemangoo.indpjsk.ui
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.unit.dp
import net.bluemangoo.indpjsk.R
import net.bluemangoo.indpjsk.data.SettingsStore
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun UploadSettingsScreen(onBack: () -> Unit) {
val context = LocalContext.current
var code by remember { mutableStateOf(SettingsStore.loadUploadCode(context)) }
var useUpload by remember { mutableStateOf(SettingsStore.loadUseUpload(context)) }
var showCode by remember { mutableStateOf(false) }
val canUpload = code.isNotBlank()
Scaffold(
topBar = {
TopAppBar(
title = { Text("上传设置") },
navigationIcon = {
IconButton(onClick = onBack) {
Icon(
painter = painterResource(R.drawable.arrow_back_24px),
contentDescription = "返回"
)
}
}
)
}
) { padding ->
Column(
modifier = Modifier
.padding(padding)
.verticalScroll(rememberScrollState())
.padding(horizontal = 16.dp, vertical = 12.dp),
verticalArrangement = Arrangement.spacedBy(12.dp)
) {
OutlinedTextField(
value = code,
onValueChange = {
code = it.trim()
SettingsStore.saveUploadCode(context, code)
if (code.isBlank() && useUpload) {
useUpload = false
SettingsStore.saveUseUpload(context, false)
}
},
modifier = Modifier.fillMaxWidth(),
singleLine = true,
label = { Text("Haruki 上传码") },
placeholder = { Text("从 Haruki 工具箱 - 数据上传 - iOS 获取") },
visualTransformation = if (showCode) {
VisualTransformation.None
} else {
PasswordVisualTransformation()
},
trailingIcon = {
TextButton(onClick = { showCode = !showCode }) {
Text(if (showCode) "隐藏" else "显示")
}
}
)
Row(
modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically
) {
Checkbox(
checked = useUpload && canUpload,
onCheckedChange = {
useUpload = it
SettingsStore.saveUseUpload(context, it)
},
enabled = canUpload
)
Text(
text = "上传到 Haruki",
style = MaterialTheme.typography.bodyMedium
)
}
}
}
}