mirror of
https://github.com/wgtunnel/android.git
synced 2026-07-03 14:07:49 +02:00
fix: more ui fixes, move ping targets
This commit is contained in:
@@ -73,7 +73,7 @@
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:exported="true"
|
||||
android:windowSoftInputMode="adjustNothing"
|
||||
android:windowSoftInputMode="adjustResize"
|
||||
android:theme="@style/Theme.WireguardAutoTunnel"
|
||||
android:configChanges="orientation|screenSize|keyboardHidden"
|
||||
>
|
||||
|
||||
@@ -71,6 +71,7 @@ import com.zaneschepke.wireguardautotunnel.ui.screens.settings.dns.DnsSettingsSc
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.settings.globals.TunnelGlobalsScreen
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.settings.monitoring.TunnelMonitoringScreen
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.settings.monitoring.logs.LogsScreen
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.settings.monitoring.ping.PingTargetScreen
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.settings.system.AndroidIntegrationsScreen
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.support.SupportScreen
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.support.donate.DonateScreen
|
||||
@@ -449,6 +450,7 @@ class MainActivity : AppCompatActivity() {
|
||||
entry<Route.PreferredTunnel> { key ->
|
||||
PreferredTunnelScreen(key.tunnelNetwork)
|
||||
}
|
||||
entry<Route.PingTarget> { PingTargetScreen() }
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
-1
@@ -102,7 +102,6 @@ class AutoTunnelControlTile : TileService(), LifecycleOwner {
|
||||
return ret
|
||||
}
|
||||
|
||||
|
||||
override val lifecycle: Lifecycle
|
||||
get() = lifecycleRegistry
|
||||
}
|
||||
|
||||
-1
@@ -180,7 +180,6 @@ class TunnelControlTile : TileService(), LifecycleOwner {
|
||||
return ret
|
||||
}
|
||||
|
||||
|
||||
override val lifecycle: Lifecycle
|
||||
get() = lifecycleRegistry
|
||||
}
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
package com.zaneschepke.wireguardautotunnel.ui.common
|
||||
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
@Composable
|
||||
fun SectionDivider() {
|
||||
HorizontalDivider(
|
||||
color = MaterialTheme.colorScheme.outline.copy(0.30f),
|
||||
modifier = Modifier.padding(horizontal = 12.dp),
|
||||
)
|
||||
}
|
||||
+2
-4
@@ -76,8 +76,7 @@ fun SurfaceRow(
|
||||
) {
|
||||
Row(
|
||||
modifier =
|
||||
Modifier.weight(1f)
|
||||
.focusRequester(mainFocusRequester)
|
||||
Modifier.focusRequester(mainFocusRequester)
|
||||
.focusProperties { right = trailingFocusRequester }
|
||||
.run {
|
||||
if (onClick != null) {
|
||||
@@ -108,7 +107,7 @@ fun SurfaceRow(
|
||||
}
|
||||
}
|
||||
Column(
|
||||
modifier = Modifier.padding(end = 16.dp),
|
||||
modifier = Modifier.padding(end = 16.dp).weight(1f),
|
||||
verticalArrangement = Arrangement.spacedBy(4.dp),
|
||||
) {
|
||||
Text(
|
||||
@@ -122,7 +121,6 @@ fun SurfaceRow(
|
||||
description()
|
||||
}
|
||||
}
|
||||
Spacer(Modifier.weight(1f))
|
||||
if (trailing != null) {
|
||||
trailing(
|
||||
Modifier.focusRequester(trailingFocusRequester).focusProperties {
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package com.zaneschepke.wireguardautotunnel.ui.common
|
||||
package com.zaneschepke.wireguardautotunnel.ui.common.security
|
||||
|
||||
import android.view.WindowManager
|
||||
import androidx.compose.runtime.Composable
|
||||
+1
-5
@@ -3,15 +3,12 @@ package com.zaneschepke.wireguardautotunnel.ui.common.textbox
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.imePadding
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
@@ -46,8 +43,7 @@ fun ConfigurationTextBox(
|
||||
isError = isError,
|
||||
textStyle =
|
||||
MaterialTheme.typography.bodySmall.copy(color = MaterialTheme.colorScheme.onSurface),
|
||||
modifier =
|
||||
modifier.fillMaxWidth().height(48.dp).focusRequester(focusRequester).imePadding(),
|
||||
modifier = modifier.fillMaxWidth().height(48.dp).focusRequester(focusRequester),
|
||||
value = value,
|
||||
visualTransformation = visualTransformation,
|
||||
singleLine = singleLine,
|
||||
|
||||
+16
-5
@@ -10,7 +10,12 @@ import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.OutlinedTextFieldDefaults
|
||||
import androidx.compose.material3.TextFieldDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.focus.onFocusChanged
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.SolidColor
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
@@ -41,6 +46,11 @@ fun CustomTextField(
|
||||
interactionSource: MutableInteractionSource = MutableInteractionSource(),
|
||||
) {
|
||||
val space = " "
|
||||
var isFocused by remember { mutableStateOf(false) }
|
||||
val cursorBrush =
|
||||
if (isFocused) SolidColor(MaterialTheme.colorScheme.primary)
|
||||
else SolidColor(Color.Transparent)
|
||||
|
||||
BasicTextField(
|
||||
value = value,
|
||||
textStyle = textStyle,
|
||||
@@ -48,8 +58,8 @@ fun CustomTextField(
|
||||
keyboardActions = keyboardActions,
|
||||
keyboardOptions = keyboardOptions,
|
||||
readOnly = readOnly,
|
||||
cursorBrush = SolidColor(MaterialTheme.colorScheme.onSurface),
|
||||
modifier = modifier,
|
||||
cursorBrush = cursorBrush,
|
||||
modifier = modifier.onFocusChanged { focusState -> isFocused = focusState.isFocused },
|
||||
interactionSource = interactionSource,
|
||||
enabled = enabled,
|
||||
singleLine = singleLine,
|
||||
@@ -79,7 +89,7 @@ fun CustomTextField(
|
||||
focusedContainerColor = containerColor,
|
||||
unfocusedContainerColor = containerColor,
|
||||
focusedTextColor = MaterialTheme.colorScheme.onSurface,
|
||||
cursorColor = MaterialTheme.colorScheme.onSurface,
|
||||
cursorColor = MaterialTheme.colorScheme.primary,
|
||||
),
|
||||
enabled = enabled,
|
||||
label = label,
|
||||
@@ -97,12 +107,13 @@ fun CustomTextField(
|
||||
errorContainerColor = containerColor,
|
||||
disabledLabelColor = MaterialTheme.colorScheme.onSurface,
|
||||
disabledContainerColor = containerColor,
|
||||
focusedIndicatorColor = MaterialTheme.colorScheme.onSurface,
|
||||
focusedIndicatorColor = MaterialTheme.colorScheme.primary,
|
||||
unfocusedIndicatorColor = MaterialTheme.colorScheme.outline,
|
||||
focusedLabelColor = MaterialTheme.colorScheme.onSurface,
|
||||
focusedContainerColor = containerColor,
|
||||
unfocusedContainerColor = containerColor,
|
||||
focusedTextColor = MaterialTheme.colorScheme.onSurface,
|
||||
cursorColor = MaterialTheme.colorScheme.onSurface,
|
||||
cursorColor = MaterialTheme.colorScheme.primary,
|
||||
),
|
||||
shape = RoundedCornerShape(8.dp),
|
||||
focusedBorderThickness = 0.5.dp,
|
||||
|
||||
@@ -73,6 +73,8 @@ sealed class Route : NavKey {
|
||||
@Keep @Serializable data object Addresses : Route()
|
||||
|
||||
@Keep @Serializable data class PreferredTunnel(val tunnelNetwork: TunnelNetwork) : Route()
|
||||
|
||||
@Keep @Serializable data object PingTarget : Route()
|
||||
}
|
||||
|
||||
@Serializable
|
||||
@@ -126,6 +128,7 @@ enum class Tab(
|
||||
Route.Appearance,
|
||||
Route.Language,
|
||||
Route.Display,
|
||||
Route.PingTarget,
|
||||
Route.Logs -> SETTINGS
|
||||
is Route.Support,
|
||||
Route.License,
|
||||
|
||||
+13
@@ -542,6 +542,19 @@ fun currentRouteAsNavbarState(
|
||||
showBottomItems = true,
|
||||
)
|
||||
}
|
||||
PingTarget ->
|
||||
NavbarState(
|
||||
topLeading = {
|
||||
IconButton(onClick = { navController.pop() }) {
|
||||
Icon(
|
||||
Icons.AutoMirrored.Rounded.ArrowBack,
|
||||
stringResource(R.string.back),
|
||||
)
|
||||
}
|
||||
},
|
||||
topTitle = context.getString(R.string.ping_target),
|
||||
showBottomItems = true,
|
||||
)
|
||||
null -> NavbarState()
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -45,7 +45,7 @@ fun TrustedNetworkTextBox(
|
||||
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(10.dp),
|
||||
modifier = modifier.animateContentSize().imePadding(),
|
||||
modifier = modifier.animateContentSize(),
|
||||
) {
|
||||
FlowRow(
|
||||
modifier = Modifier.fillMaxWidth().animateContentSize(),
|
||||
|
||||
+3
-25
@@ -2,18 +2,11 @@ package com.zaneschepke.wireguardautotunnel.ui.screens.autotunnel.preferred
|
||||
|
||||
import androidx.compose.foundation.gestures.ScrollableDefaults
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.FlowRow
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.imePadding
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.foundation.overscroll
|
||||
import androidx.compose.foundation.relocation.BringIntoViewRequester
|
||||
import androidx.compose.foundation.relocation.bringIntoViewRequester
|
||||
import androidx.compose.foundation.rememberOverscrollEffect
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
@@ -25,12 +18,7 @@ import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
@@ -182,17 +170,10 @@ fun PreferredTunnelScreen(
|
||||
}
|
||||
if (selectedTunnel?.id == tunnel.id) {
|
||||
val focusRequester = remember { FocusRequester() }
|
||||
val bringIntoViewRequester = remember {
|
||||
BringIntoViewRequester()
|
||||
}
|
||||
val keyboardController = LocalSoftwareKeyboardController.current
|
||||
|
||||
var currentText by remember { mutableStateOf("") }
|
||||
|
||||
LaunchedEffect(tunnel.tunnelNetworks.size) {
|
||||
bringIntoViewRequester.bringIntoView()
|
||||
}
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
focusRequester.requestFocus()
|
||||
keyboardController?.show()
|
||||
@@ -219,10 +200,7 @@ fun PreferredTunnelScreen(
|
||||
{ WildcardsLabel() }
|
||||
} else null,
|
||||
modifier =
|
||||
Modifier.fillMaxWidth()
|
||||
.focusRequester(focusRequester)
|
||||
.bringIntoViewRequester(bringIntoViewRequester)
|
||||
.imePadding(),
|
||||
Modifier.fillMaxWidth().focusRequester(focusRequester),
|
||||
singleLine = true,
|
||||
keyboardOptions =
|
||||
KeyboardOptions(
|
||||
|
||||
+6
@@ -7,6 +7,7 @@ import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.Adjust
|
||||
import androidx.compose.material.icons.outlined.NetworkPing
|
||||
import androidx.compose.material.icons.outlined.QueryStats
|
||||
import androidx.compose.material.icons.outlined.Replay
|
||||
@@ -110,6 +111,11 @@ fun TunnelMonitoringScreen(viewModel: SettingsViewModel = hiltViewModel()) {
|
||||
},
|
||||
onClick = { viewModel.setDetailedPingStats(!settingsState.showDetailedPingStats) },
|
||||
)
|
||||
SurfaceRow(
|
||||
leading = { Icon(Icons.Outlined.Adjust, contentDescription = null) },
|
||||
title = stringResource(R.string.set_custom_ping_target),
|
||||
onClick = { navController.push(Route.PingTarget) },
|
||||
)
|
||||
}
|
||||
Column {
|
||||
GroupLabel(
|
||||
|
||||
+163
@@ -0,0 +1,163 @@
|
||||
package com.zaneschepke.wireguardautotunnel.ui.screens.settings.monitoring.ping
|
||||
|
||||
import android.util.Patterns
|
||||
import androidx.compose.foundation.gestures.ScrollableDefaults
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.foundation.overscroll
|
||||
import androidx.compose.foundation.rememberOverscrollEffect
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.Save
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.text.input.KeyboardCapitalization
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.zaneschepke.wireguardautotunnel.R
|
||||
import com.zaneschepke.wireguardautotunnel.domain.model.TunnelConf
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.SurfaceRow
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.label.GroupLabel
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.text.DescriptionText
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.textbox.CustomTextField
|
||||
import com.zaneschepke.wireguardautotunnel.util.extensions.isValidIpv4orIpv6Address
|
||||
import com.zaneschepke.wireguardautotunnel.viewmodel.SettingsViewModel
|
||||
|
||||
@Composable
|
||||
fun PingTargetScreen(viewModel: SettingsViewModel = hiltViewModel()) {
|
||||
|
||||
val settingsState by viewModel.container.stateFlow.collectAsStateWithLifecycle()
|
||||
|
||||
if (settingsState.isLoading) return
|
||||
|
||||
var selectedTunnel by remember { mutableStateOf<TunnelConf?>(null) }
|
||||
|
||||
var isError by remember { mutableStateOf<Boolean>(false) }
|
||||
|
||||
LazyColumn(
|
||||
horizontalAlignment = Alignment.Start,
|
||||
verticalArrangement = Arrangement.Top,
|
||||
modifier = Modifier.overscroll(rememberOverscrollEffect()),
|
||||
state = rememberLazyListState(),
|
||||
userScrollEnabled = true,
|
||||
reverseLayout = false,
|
||||
flingBehavior = ScrollableDefaults.flingBehavior(),
|
||||
) {
|
||||
item {
|
||||
DescriptionText(
|
||||
stringResource(R.string.ping_target_description),
|
||||
modifier = Modifier.padding(horizontal = 16.dp).padding(bottom = 16.dp),
|
||||
)
|
||||
GroupLabel(
|
||||
stringResource(R.string.tunnels),
|
||||
modifier = Modifier.padding(horizontal = 16.dp),
|
||||
)
|
||||
}
|
||||
items(settingsState.tunnels, key = { it.id }) { tunnel ->
|
||||
SurfaceRow(
|
||||
title = tunnel.tunName,
|
||||
onClick = { selectedTunnel = tunnel },
|
||||
description = {
|
||||
DescriptionText(
|
||||
stringResource(
|
||||
R.string.ping_target_template,
|
||||
tunnel.pingTarget ?: stringResource(R.string._default),
|
||||
)
|
||||
)
|
||||
},
|
||||
expandedContent = {
|
||||
if (selectedTunnel?.id == tunnel.id) {
|
||||
val focusRequester = remember { FocusRequester() }
|
||||
val keyboardController = LocalSoftwareKeyboardController.current
|
||||
|
||||
var currentText by remember { mutableStateOf(tunnel.pingTarget ?: "") }
|
||||
var isError by remember { mutableStateOf(false) }
|
||||
|
||||
LaunchedEffect(currentText) { isError = false }
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
focusRequester.requestFocus()
|
||||
keyboardController?.show()
|
||||
}
|
||||
|
||||
fun onClick() {
|
||||
val isValid =
|
||||
currentText.isBlank() ||
|
||||
currentText.isValidIpv4orIpv6Address() ||
|
||||
Patterns.DOMAIN_NAME.matcher(currentText).matches()
|
||||
if (!isValid) {
|
||||
isError = true
|
||||
return
|
||||
}
|
||||
viewModel.setPingTarget(tunnel, currentText)
|
||||
selectedTunnel = null
|
||||
}
|
||||
|
||||
CustomTextField(
|
||||
isError = isError,
|
||||
textStyle =
|
||||
MaterialTheme.typography.bodySmall.copy(
|
||||
color = MaterialTheme.colorScheme.onSurface
|
||||
),
|
||||
value = currentText,
|
||||
onValueChange = { currentText = it },
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
label = {
|
||||
Text(
|
||||
stringResource(R.string.set_ping_target),
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
style = MaterialTheme.typography.labelMedium,
|
||||
)
|
||||
},
|
||||
containerColor = MaterialTheme.colorScheme.surface,
|
||||
modifier =
|
||||
Modifier.fillMaxWidth()
|
||||
.focusRequester(focusRequester)
|
||||
.padding(top = 8.dp),
|
||||
singleLine = true,
|
||||
keyboardOptions =
|
||||
KeyboardOptions(
|
||||
capitalization = KeyboardCapitalization.None,
|
||||
imeAction = ImeAction.Done,
|
||||
),
|
||||
keyboardActions = KeyboardActions(onDone = { onClick() }),
|
||||
trailing = {
|
||||
if (currentText != "") {
|
||||
IconButton(onClick = { onClick() }) {
|
||||
Icon(
|
||||
imageVector = Icons.Outlined.Save,
|
||||
contentDescription = stringResource(R.string.save),
|
||||
tint = MaterialTheme.colorScheme.primary,
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -24,10 +24,10 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.zaneschepke.wireguardautotunnel.R
|
||||
import com.zaneschepke.wireguardautotunnel.domain.model.AppProxySettings
|
||||
import com.zaneschepke.wireguardautotunnel.ui.LocalSharedVm
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.SecureScreenFromRecording
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.ScaledSwitch
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.SurfaceRow
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.label.GroupLabel
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.security.SecureScreenFromRecording
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.textbox.ConfigurationTextBox
|
||||
import com.zaneschepke.wireguardautotunnel.ui.sideeffect.LocalSideEffect
|
||||
import com.zaneschepke.wireguardautotunnel.viewmodel.ProxySettingsViewModel
|
||||
|
||||
+1
-1
@@ -29,11 +29,11 @@ import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.zaneschepke.wireguardautotunnel.R
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.SecureScreenFromRecording
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.ScaledSwitch
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.SurfaceRow
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.functions.rememberClipboardHelper
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.label.GroupLabel
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.security.SecureScreenFromRecording
|
||||
import com.zaneschepke.wireguardautotunnel.util.extensions.launchVpnSettings
|
||||
import com.zaneschepke.wireguardautotunnel.viewmodel.SettingsViewModel
|
||||
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@ import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.zaneschepke.wireguardautotunnel.data.entity.TunnelConfig
|
||||
import com.zaneschepke.wireguardautotunnel.ui.LocalSharedVm
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.SecureScreenFromRecording
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.security.SecureScreenFromRecording
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.tunnels.config.components.AddPeerButton
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.tunnels.config.components.InterfaceSection
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.tunnels.config.components.PeersSection
|
||||
|
||||
+1
-91
@@ -1,23 +1,16 @@
|
||||
package com.zaneschepke.wireguardautotunnel.ui.screens.tunnels.tunneloptions
|
||||
|
||||
import android.util.Patterns
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.outlined.CallSplit
|
||||
import androidx.compose.material.icons.outlined.Dns
|
||||
import androidx.compose.material.icons.outlined.Save
|
||||
import androidx.compose.material.icons.outlined.Star
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.*
|
||||
@@ -25,12 +18,8 @@ import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.text.input.KeyboardCapitalization
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.zaneschepke.wireguardautotunnel.R
|
||||
@@ -40,11 +29,9 @@ import com.zaneschepke.wireguardautotunnel.ui.common.button.ScaledSwitch
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.SurfaceRow
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.label.GroupLabel
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.text.DescriptionText
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.textbox.CustomTextField
|
||||
import com.zaneschepke.wireguardautotunnel.ui.navigation.Route
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.tunnels.tunneloptions.components.*
|
||||
import com.zaneschepke.wireguardautotunnel.ui.sideeffect.LocalSideEffect
|
||||
import com.zaneschepke.wireguardautotunnel.util.extensions.isValidIpv4orIpv6Address
|
||||
import com.zaneschepke.wireguardautotunnel.viewmodel.TunnelViewModel
|
||||
import org.orbitmvi.orbit.compose.collectSideEffect
|
||||
|
||||
@@ -108,7 +95,7 @@ fun TunnelOptionsScreen(viewModel: TunnelViewModel) {
|
||||
}
|
||||
Column {
|
||||
GroupLabel(
|
||||
stringResource(R.string.reliability),
|
||||
stringResource(R.string.other),
|
||||
modifier = Modifier.padding(horizontal = 16.dp),
|
||||
)
|
||||
SurfaceRow(
|
||||
@@ -138,83 +125,6 @@ fun TunnelOptionsScreen(viewModel: TunnelViewModel) {
|
||||
},
|
||||
onClick = { viewModel.toggleIpv4Preferred() },
|
||||
)
|
||||
SurfaceRow(
|
||||
title = "",
|
||||
description = {
|
||||
val focusManager = LocalFocusManager.current
|
||||
val keyboardController = LocalSoftwareKeyboardController.current
|
||||
|
||||
var stateValue by remember { mutableStateOf(tunnel.pingTarget ?: "") }
|
||||
val isError by
|
||||
remember(stateValue) {
|
||||
derivedStateOf {
|
||||
stateValue.isNotBlank() &&
|
||||
!stateValue.isValidIpv4orIpv6Address() &&
|
||||
!Patterns.DOMAIN_NAME.matcher(stateValue).matches()
|
||||
}
|
||||
}
|
||||
Column(verticalArrangement = Arrangement.spacedBy(16.dp)) {
|
||||
CustomTextField(
|
||||
isError = isError,
|
||||
textStyle =
|
||||
MaterialTheme.typography.bodySmall.copy(
|
||||
color = MaterialTheme.colorScheme.onSurface
|
||||
),
|
||||
value = stateValue,
|
||||
onValueChange = { stateValue = it },
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
label = {
|
||||
Text(
|
||||
stringResource(R.string.set_custom_ping_target),
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
style = MaterialTheme.typography.labelMedium,
|
||||
)
|
||||
},
|
||||
placeholder = {
|
||||
Text(
|
||||
stringResource(R.string.ip_or_hostname),
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.outline,
|
||||
)
|
||||
},
|
||||
containerColor = MaterialTheme.colorScheme.surface,
|
||||
supportingText = {
|
||||
Text(stringResource(R.string.ping_target_description))
|
||||
},
|
||||
modifier =
|
||||
Modifier.padding(top = 5.dp, bottom = 10.dp)
|
||||
.fillMaxWidth()
|
||||
.padding(end = 16.dp),
|
||||
singleLine = true,
|
||||
keyboardOptions =
|
||||
KeyboardOptions(
|
||||
capitalization = KeyboardCapitalization.None,
|
||||
imeAction = ImeAction.Done,
|
||||
),
|
||||
keyboardActions =
|
||||
KeyboardActions(onDone = { viewModel.setTunnelPingIp(stateValue) }),
|
||||
trailing = {
|
||||
if (!isError) {
|
||||
IconButton(
|
||||
onClick = {
|
||||
viewModel.setTunnelPingIp(stateValue)
|
||||
keyboardController?.hide()
|
||||
focusManager.clearFocus()
|
||||
}
|
||||
) {
|
||||
val icon = Icons.Outlined.Save
|
||||
Icon(
|
||||
imageVector = icon,
|
||||
contentDescription = icon.name,
|
||||
tint = MaterialTheme.colorScheme.primary,
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,4 +12,5 @@ data class SettingUiState(
|
||||
val showDetailedPingStats: Boolean = false,
|
||||
val isLoading: Boolean = true,
|
||||
val globalTunnelConf: TunnelConf? = null,
|
||||
val tunnels: List<TunnelConf> = emptyList(),
|
||||
)
|
||||
|
||||
@@ -10,10 +10,10 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.SideEffect
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.toArgb
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
import androidx.core.view.WindowCompat
|
||||
import androidx.core.view.WindowInsetsControllerCompat
|
||||
import com.zaneschepke.wireguardautotunnel.ui.LocalIsAndroidTV
|
||||
|
||||
private val DarkColorScheme =
|
||||
@@ -94,16 +94,13 @@ fun WireguardAutoTunnelTheme(theme: Theme = Theme.AUTOMATIC, content: @Composabl
|
||||
|
||||
val view = LocalView.current
|
||||
if (!view.isInEditMode) {
|
||||
@Suppress("DEPRECATION")
|
||||
SideEffect {
|
||||
val window = (view.context as Activity).window
|
||||
WindowCompat.setDecorFitsSystemWindows(window, false)
|
||||
window.navigationBarColor = Color.Transparent.toArgb()
|
||||
window.statusBarColor = Color.Transparent.toArgb()
|
||||
WindowCompat.getInsetsController(window, window.decorView).apply {
|
||||
isAppearanceLightStatusBars = !isDark
|
||||
isAppearanceLightNavigationBars = !isDark
|
||||
}
|
||||
// For API 33+, use WindowInsetsControllerCompat for appearance control
|
||||
val insetsController = WindowInsetsControllerCompat(window, window.decorView)
|
||||
insetsController.isAppearanceLightStatusBars = !isDark
|
||||
insetsController.isAppearanceLightNavigationBars = !isDark
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+8
-2
@@ -39,8 +39,9 @@ constructor(
|
||||
settingsRepository.flow,
|
||||
appStateRepository.flow,
|
||||
tunnelsRepository.globalTunnelFlow,
|
||||
) { settings, appState, tunnel ->
|
||||
SettingUiState(
|
||||
tunnelsRepository.userTunnelsFlow,
|
||||
) { settings, appState, tunnel, tunnels ->
|
||||
state.copy(
|
||||
settings = settings,
|
||||
isLocalLoggingEnabled = appState.isLocalLogsEnabled,
|
||||
remoteKey = appState.remoteKey,
|
||||
@@ -49,6 +50,7 @@ constructor(
|
||||
showDetailedPingStats = appState.showDetailedPingStats,
|
||||
isLoading = false,
|
||||
globalTunnelConf = tunnel,
|
||||
tunnels = tunnels,
|
||||
)
|
||||
}
|
||||
.collect { reduce { it } }
|
||||
@@ -113,6 +115,10 @@ constructor(
|
||||
appStateRepository.setIsRemoteControlEnabled(to)
|
||||
}
|
||||
|
||||
fun setPingTarget(tunnel: TunnelConf, target: String?) = intent {
|
||||
tunnelsRepository.save(tunnel.copy(pingTarget = target?.ifBlank { null }))
|
||||
}
|
||||
|
||||
fun setDnsProvider(dnsProvider: DnsProvider) = intent {
|
||||
settingsRepository.save(
|
||||
state.settings.copy(dnsEndpoint = dnsProvider.asAddress(state.settings.dnsProtocol))
|
||||
|
||||
@@ -272,11 +272,9 @@
|
||||
<string name="pinger_bounce_recovery">Pinger restart failed. Tunnel recovered with old addresses.</string>
|
||||
<string name="bounce_failed_template">Pinger failed to recovery tunnel with error: %1$s</string>
|
||||
<string name="tunnel_monitoring">Tunnel monitoring</string>
|
||||
<string name="set_custom_ping_target">Custom ping target (optional)</string>
|
||||
<string name="set_custom_ping_target">Custom ping targets</string>
|
||||
<string name="ip_or_hostname">IP or hostname</string>
|
||||
<string name="ping_target_description">Used if no internal IP is available (e.g., in full-tunnel configs). Leaving
|
||||
blank defaults to Cloudflare.
|
||||
</string>
|
||||
<string name="ping_target_description">Defaults to an internal tunnel IP for split configurations or Cloudflare for full tunnel configurations.</string>
|
||||
<string name="tunnel_ping_interval">Tunnel ping interval</string>
|
||||
<string name="attempts_per_interval">Ping attempts per interval</string>
|
||||
<string name="ping_timeout">Tunnel ping timeout</string>
|
||||
@@ -411,5 +409,6 @@
|
||||
<string name="ping">Ping</string>
|
||||
<string name="ping_monitor">Ping monitor</string>
|
||||
<string name="backup_and_restore">Backup and restore</string>
|
||||
<string name="reliability">Reliability</string>
|
||||
<string name="ping_target">Ping target</string>
|
||||
<string name="set_ping_target">Set ping target</string>
|
||||
</resources>
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
<item name="android:windowAllowEnterTransitionOverlap">true</item>
|
||||
<item name="android:statusBarColor">@android:color/transparent</item>
|
||||
<item name="android:navigationBarColor">@android:color/transparent</item>
|
||||
<item name="android:windowTranslucentStatus">true</item>
|
||||
<item name="android:windowTranslucentNavigation">true</item>
|
||||
</style>
|
||||
|
||||
<style name="Theme.App.Start" parent="@style/Theme.SplashScreen">
|
||||
|
||||
Reference in New Issue
Block a user