summaryrefslogtreecommitdiffhomepage
path: root/android/app/src
diff options
context:
space:
mode:
authorDavid Göransson <david.goransson@mullvad.net>2024-06-04 08:27:30 +0200
committerDavid Göransson <david.goransson@mullvad.net>2024-06-04 08:27:30 +0200
commit45c369fe355a5eadc29dd3e6f492ff6d7430ee5d (patch)
tree70925bd8ab3348533ca7217606ac4c2d42356453 /android/app/src
parent3ac0f264402e42a16f0bd6c76e1a6a67e01ed4ef (diff)
parent571c620b51cdc6b4338a3244b500f70e79b3ca7d (diff)
downloadmullvadvpn-45c369fe355a5eadc29dd3e6f492ff6d7430ee5d.tar.xz
mullvadvpn-45c369fe355a5eadc29dd3e6f492ff6d7430ee5d.zip
Merge branch 'add-setting-for-udp2tcp-port-droid-249'
Diffstat (limited to 'android/app/src')
-rw-r--r--android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/VpnSettingsScreenTest.kt98
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/ExpandableComposeCell.kt4
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/SelectableCell.kt18
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/SwitchComposeCell.kt6
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/VpnSettingsScreen.kt52
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/VpnSettingsUiState.kt4
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/test/ComposeTestTagConstants.kt6
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/constant/WireguardConstant.kt1
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/repository/SettingsRepository.kt10
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/VpnSettingsViewModel.kt28
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/VpnSettingsViewModelState.kt3
-rw-r--r--android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/VpnSettingsViewModelTest.kt13
12 files changed, 220 insertions, 23 deletions
diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/VpnSettingsScreenTest.kt b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/VpnSettingsScreenTest.kt
index ca7a01a0a9..650c51d641 100644
--- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/VpnSettingsScreenTest.kt
+++ b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/VpnSettingsScreenTest.kt
@@ -8,6 +8,7 @@ import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import androidx.compose.ui.test.performScrollToNode
import io.mockk.MockKAnnotations
+import io.mockk.coVerify
import io.mockk.mockk
import io.mockk.verify
import net.mullvad.mullvadvpn.compose.createEdgeToEdgeComposeExtension
@@ -17,6 +18,8 @@ import net.mullvad.mullvadvpn.compose.test.LAZY_LIST_LAST_ITEM_TEST_TAG
import net.mullvad.mullvadvpn.compose.test.LAZY_LIST_QUANTUM_ITEM_OFF_TEST_TAG
import net.mullvad.mullvadvpn.compose.test.LAZY_LIST_QUANTUM_ITEM_ON_TEST_TAG
import net.mullvad.mullvadvpn.compose.test.LAZY_LIST_TEST_TAG
+import net.mullvad.mullvadvpn.compose.test.LAZY_LIST_UDP_OVER_TCP_PORT_ITEM_X_TEST_TAG
+import net.mullvad.mullvadvpn.compose.test.LAZY_LIST_UDP_OVER_TCP_PORT_TEST_TAG
import net.mullvad.mullvadvpn.compose.test.LAZY_LIST_WIREGUARD_CUSTOM_PORT_NUMBER_TEST_TAG
import net.mullvad.mullvadvpn.compose.test.LAZY_LIST_WIREGUARD_CUSTOM_PORT_TEXT_TEST_TAG
import net.mullvad.mullvadvpn.compose.test.LAZY_LIST_WIREGUARD_PORT_ITEM_X_TEST_TAG
@@ -25,6 +28,7 @@ import net.mullvad.mullvadvpn.lib.model.Mtu
import net.mullvad.mullvadvpn.lib.model.Port
import net.mullvad.mullvadvpn.lib.model.PortRange
import net.mullvad.mullvadvpn.lib.model.QuantumResistantState
+import net.mullvad.mullvadvpn.lib.model.SelectedObfuscation
import net.mullvad.mullvadvpn.onNodeWithTagAndText
import net.mullvad.mullvadvpn.viewmodel.CustomDnsItem
import org.junit.jupiter.api.BeforeEach
@@ -207,6 +211,77 @@ class VpnSettingsScreenTest {
}
@Test
+ fun testSelectTcpOverUdpPortOption() =
+ composeExtension.use {
+ // Arrange
+ val onObfuscationPortSelected: (Constraint<Port>) -> Unit = mockk(relaxed = true)
+ setContentWithTheme {
+ VpnSettingsScreen(
+ state =
+ VpnSettingsUiState.createDefault(
+ selectedObfuscation = SelectedObfuscation.Udp2Tcp,
+ selectedObfuscationPort = Constraint.Only(Port(5001))
+ ),
+ onObfuscationPortSelected = onObfuscationPortSelected
+ )
+ }
+
+ // Act
+ onNodeWithTag(LAZY_LIST_TEST_TAG)
+ .performScrollToNode(hasTestTag(LAZY_LIST_UDP_OVER_TCP_PORT_TEST_TAG))
+ onNodeWithText("UDP-over-TCP port").performClick()
+ onNodeWithTag(LAZY_LIST_TEST_TAG)
+ .performScrollToNode(
+ hasTestTag(String.format(LAZY_LIST_UDP_OVER_TCP_PORT_ITEM_X_TEST_TAG, 5001))
+ )
+
+ // Assert
+ onNodeWithTagAndText(
+ testTag = String.format(LAZY_LIST_UDP_OVER_TCP_PORT_ITEM_X_TEST_TAG, 5001),
+ text = "5001"
+ )
+ .assertExists()
+ .performClick()
+
+ coVerify(exactly = 1) { onObfuscationPortSelected.invoke(Constraint.Only(Port(5001))) }
+ }
+
+ @Test
+ fun testAttemptSelectTcpOverUdpPortOption() =
+ composeExtension.use {
+ // Arrange
+ val onObfuscationPortSelected: (Constraint<Port>) -> Unit = mockk(relaxed = true)
+ setContentWithTheme {
+ VpnSettingsScreen(
+ state =
+ VpnSettingsUiState.createDefault(
+ selectedObfuscation = SelectedObfuscation.Off,
+ ),
+ onObfuscationPortSelected = onObfuscationPortSelected
+ )
+ }
+
+ // Act
+ onNodeWithTag(LAZY_LIST_TEST_TAG)
+ .performScrollToNode(hasTestTag(LAZY_LIST_UDP_OVER_TCP_PORT_TEST_TAG))
+ onNodeWithText("UDP-over-TCP port").performClick()
+ onNodeWithTag(LAZY_LIST_TEST_TAG)
+ .performScrollToNode(
+ hasTestTag(String.format(LAZY_LIST_UDP_OVER_TCP_PORT_ITEM_X_TEST_TAG, 5001))
+ )
+
+ // Assert
+ onNodeWithTagAndText(
+ testTag = String.format(LAZY_LIST_UDP_OVER_TCP_PORT_ITEM_X_TEST_TAG, 5001),
+ text = "5001"
+ )
+ .assertExists()
+ .performClick()
+
+ verify(exactly = 0) { onObfuscationPortSelected.invoke(any()) }
+ }
+
+ @Test
fun testShowSelectedTunnelQuantumOption() =
composeExtension.use {
// Arrange
@@ -402,6 +477,29 @@ class VpnSettingsScreenTest {
}
@Test
+ fun testShowObfuscationInfo() =
+ composeExtension.use {
+ val mockedNavigateToObfuscationInfo: () -> Unit = mockk(relaxed = true)
+
+ // Arrange
+ setContentWithTheme {
+ VpnSettingsScreen(
+ state = VpnSettingsUiState.createDefault(),
+ navigateToObfuscationInfo = mockedNavigateToObfuscationInfo
+ )
+ }
+
+ // Act
+
+ onNodeWithTag(LAZY_LIST_TEST_TAG)
+ .performScrollToNode(hasTestTag(LAZY_LIST_UDP_OVER_TCP_PORT_TEST_TAG))
+ onNodeWithText("WireGuard obfuscation").performClick()
+
+ // Assert
+ verify(exactly = 1) { mockedNavigateToObfuscationInfo() }
+ }
+
+ @Test
fun testShowTunnelQuantumInfo() =
composeExtension.use {
val mockedShowTunnelQuantumInfoClick: () -> Unit = mockk(relaxed = true)
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/ExpandableComposeCell.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/ExpandableComposeCell.kt
index 73a6a5283d..b19f534456 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/ExpandableComposeCell.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/ExpandableComposeCell.kt
@@ -13,6 +13,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.focus.focusProperties
+import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
@@ -46,6 +47,7 @@ fun ExpandableComposeCell(
title: String,
isExpanded: Boolean,
isEnabled: Boolean = true,
+ testTag: String = "",
onCellClicked: (Boolean) -> Unit = {},
onInfoClicked: (() -> Unit)? = null
) {
@@ -53,7 +55,7 @@ fun ExpandableComposeCell(
val bodyViewModifier = Modifier
BaseCell(
- modifier = Modifier.focusProperties { canFocus = false },
+ modifier = Modifier.testTag(testTag).focusProperties { canFocus = false },
headlineContent = {
BaseCellTitle(
title = title,
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/SelectableCell.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/SelectableCell.kt
index d69194490e..a4cd9b9548 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/SelectableCell.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/SelectableCell.kt
@@ -17,6 +17,7 @@ import net.mullvad.mullvadvpn.R
import net.mullvad.mullvadvpn.compose.component.SpacedColumn
import net.mullvad.mullvadvpn.lib.theme.AppTheme
import net.mullvad.mullvadvpn.lib.theme.Dimens
+import net.mullvad.mullvadvpn.lib.theme.color.AlphaDisabled
import net.mullvad.mullvadvpn.lib.theme.color.AlphaInvisible
import net.mullvad.mullvadvpn.lib.theme.color.AlphaVisible
import net.mullvad.mullvadvpn.lib.theme.color.selected
@@ -36,6 +37,7 @@ private fun PreviewSelectableCell() {
fun SelectableCell(
title: String,
isSelected: Boolean,
+ isEnabled: Boolean = true,
iconContentDescription: String? = null,
selectedIcon: @Composable RowScope.() -> Unit = {
Icon(
@@ -44,7 +46,10 @@ fun SelectableCell(
tint = MaterialTheme.colorScheme.onPrimary,
modifier =
Modifier.padding(end = Dimens.selectableCellTextMargin)
- .alpha(if (isSelected) AlphaVisible else AlphaInvisible)
+ .alpha(
+ if (isSelected && !isEnabled) AlphaDisabled
+ else if (isSelected) AlphaVisible else AlphaInvisible
+ )
)
},
titleStyle: TextStyle = MaterialTheme.typography.labelLarge,
@@ -56,7 +61,16 @@ fun SelectableCell(
) {
BaseCell(
onCellClicked = onCellClicked,
- headlineContent = { BaseCellTitle(title = title, style = titleStyle) },
+ isRowEnabled = isEnabled,
+ headlineContent = {
+ BaseCellTitle(
+ title = title,
+ style = titleStyle,
+ color =
+ if (isEnabled) MaterialTheme.colorScheme.onPrimary
+ else MaterialTheme.colorScheme.onPrimary.copy(AlphaDisabled)
+ )
+ },
background =
if (isSelected) {
selectedColor
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/SwitchComposeCell.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/SwitchComposeCell.kt
index f74349113c..11045fa617 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/SwitchComposeCell.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/SwitchComposeCell.kt
@@ -28,6 +28,7 @@ import net.mullvad.mullvadvpn.compose.component.textResource
import net.mullvad.mullvadvpn.compose.extensions.toAnnotatedString
import net.mullvad.mullvadvpn.lib.theme.AppTheme
import net.mullvad.mullvadvpn.lib.theme.Dimens
+import net.mullvad.mullvadvpn.lib.theme.color.AlphaDisabled
@Preview
@Composable
@@ -67,7 +68,10 @@ fun NormalSwitchComposeCell(
BaseCellTitle(
title = title,
style = MaterialTheme.typography.labelLarge,
- modifier = Modifier.weight(1f, true)
+ modifier = Modifier.weight(1f, true),
+ color =
+ if (isEnabled) MaterialTheme.colorScheme.onPrimary
+ else MaterialTheme.colorScheme.onPrimary.copy(AlphaDisabled)
)
},
isToggled = isToggled,
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/VpnSettingsScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/VpnSettingsScreen.kt
index 7eee7c1398..41b4ea8a2e 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/VpnSettingsScreen.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/VpnSettingsScreen.kt
@@ -77,6 +77,9 @@ import net.mullvad.mullvadvpn.compose.test.LAZY_LIST_LAST_ITEM_TEST_TAG
import net.mullvad.mullvadvpn.compose.test.LAZY_LIST_QUANTUM_ITEM_OFF_TEST_TAG
import net.mullvad.mullvadvpn.compose.test.LAZY_LIST_QUANTUM_ITEM_ON_TEST_TAG
import net.mullvad.mullvadvpn.compose.test.LAZY_LIST_TEST_TAG
+import net.mullvad.mullvadvpn.compose.test.LAZY_LIST_UDP_OVER_TCP_PORT_ITEM_AUTOMATIC_TEST_TAG
+import net.mullvad.mullvadvpn.compose.test.LAZY_LIST_UDP_OVER_TCP_PORT_ITEM_X_TEST_TAG
+import net.mullvad.mullvadvpn.compose.test.LAZY_LIST_UDP_OVER_TCP_PORT_TEST_TAG
import net.mullvad.mullvadvpn.compose.test.LAZY_LIST_WIREGUARD_CUSTOM_PORT_NUMBER_TEST_TAG
import net.mullvad.mullvadvpn.compose.test.LAZY_LIST_WIREGUARD_CUSTOM_PORT_TEXT_TEST_TAG
import net.mullvad.mullvadvpn.compose.test.LAZY_LIST_WIREGUARD_PORT_ITEM_X_TEST_TAG
@@ -84,6 +87,7 @@ import net.mullvad.mullvadvpn.compose.transitions.SlideInFromRightTransition
import net.mullvad.mullvadvpn.compose.util.LaunchedEffectCollect
import net.mullvad.mullvadvpn.compose.util.OnNavResultValue
import net.mullvad.mullvadvpn.compose.util.showSnackbarImmediately
+import net.mullvad.mullvadvpn.constant.UDP2TCP_PRESET_PORTS
import net.mullvad.mullvadvpn.constant.WIREGUARD_PRESET_PORTS
import net.mullvad.mullvadvpn.lib.model.Constraint
import net.mullvad.mullvadvpn.lib.model.Mtu
@@ -258,6 +262,7 @@ fun VpnSettings(
onSelectObfuscationSetting = vm::onSelectObfuscationSetting,
onSelectQuantumResistanceSetting = vm::onSelectQuantumResistanceSetting,
onWireguardPortSelected = vm::onWireguardPortSelected,
+ onObfuscationPortSelected = vm::onObfuscationPortSelected,
)
}
@@ -293,8 +298,10 @@ fun VpnSettingsScreen(
onSelectObfuscationSetting: (selectedObfuscation: SelectedObfuscation) -> Unit = {},
onSelectQuantumResistanceSetting: (quantumResistant: QuantumResistantState) -> Unit = {},
onWireguardPortSelected: (port: Constraint<Port>) -> Unit = {},
+ onObfuscationPortSelected: (port: Constraint<Port>) -> Unit = {},
) {
var expandContentBlockersState by rememberSaveable { mutableStateOf(false) }
+ var expandUdp2TcpPortSettings by rememberSaveable { mutableStateOf(false) }
val biggerPadding = 54.dp
val topPadding = 6.dp
@@ -527,7 +534,8 @@ fun VpnSettingsScreen(
itemWithDivider {
SelectableCell(
title = port.toString(),
- testTag = String.format(LAZY_LIST_WIREGUARD_PORT_ITEM_X_TEST_TAG, port),
+ testTag =
+ String.format(null, LAZY_LIST_WIREGUARD_PORT_ITEM_X_TEST_TAG, port),
isSelected = state.selectedWireguardPort.hasValue(port),
onCellClicked = { onWireguardPortSelected(Constraint.Only(Port(port))) }
)
@@ -583,6 +591,48 @@ fun VpnSettingsScreen(
}
itemWithDivider {
+ ExpandableComposeCell(
+ title = stringResource(R.string.udp_over_tcp_port_title),
+ isExpanded = expandUdp2TcpPortSettings,
+ isEnabled = state.selectedObfuscation != SelectedObfuscation.Off,
+ onInfoClicked = navigateUdp2TcpInfo,
+ onCellClicked = { expandUdp2TcpPortSettings = !expandUdp2TcpPortSettings },
+ testTag = LAZY_LIST_UDP_OVER_TCP_PORT_TEST_TAG
+ )
+ }
+
+ if (expandUdp2TcpPortSettings) {
+ itemWithDivider {
+ SelectableCell(
+ title = stringResource(id = R.string.automatic),
+ isSelected = state.selectedObfuscationPort is Constraint.Any,
+ isEnabled = state.selectObfuscationPortEnabled,
+ onCellClicked = { onObfuscationPortSelected(Constraint.Any) },
+ testTag = LAZY_LIST_UDP_OVER_TCP_PORT_ITEM_AUTOMATIC_TEST_TAG,
+ )
+ }
+
+ UDP2TCP_PRESET_PORTS.forEach { port ->
+ itemWithDivider {
+ SelectableCell(
+ title = port.toString(),
+ isSelected = state.selectedObfuscationPort.hasValue(port),
+ isEnabled = state.selectObfuscationPortEnabled,
+ onCellClicked = {
+ onObfuscationPortSelected(Constraint.Only(Port(port)))
+ },
+ testTag =
+ String.format(
+ null,
+ LAZY_LIST_UDP_OVER_TCP_PORT_ITEM_X_TEST_TAG,
+ port
+ )
+ )
+ }
+ }
+ }
+
+ itemWithDivider {
Spacer(modifier = Modifier.height(Dimens.cellLabelVerticalPadding))
InformationComposeCell(
title = stringResource(R.string.quantum_resistant_title),
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/VpnSettingsUiState.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/VpnSettingsUiState.kt
index dd9802db2c..17eb69d380 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/VpnSettingsUiState.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/VpnSettingsUiState.kt
@@ -17,12 +17,14 @@ data class VpnSettingsUiState(
val customDnsItems: List<CustomDnsItem>,
val contentBlockersOptions: DefaultDnsOptions,
val selectedObfuscation: SelectedObfuscation,
+ val selectedObfuscationPort: Constraint<Port>,
val quantumResistant: QuantumResistantState,
val selectedWireguardPort: Constraint<Port>,
val customWireguardPort: Constraint<Port>?,
val availablePortRanges: List<PortRange>,
val systemVpnSettingsAvailable: Boolean,
) {
+ val selectObfuscationPortEnabled = selectedObfuscation != SelectedObfuscation.Off
companion object {
fun createDefault(
@@ -33,6 +35,7 @@ data class VpnSettingsUiState(
customDnsItems: List<CustomDnsItem> = emptyList(),
contentBlockersOptions: DefaultDnsOptions = DefaultDnsOptions(),
selectedObfuscation: SelectedObfuscation = SelectedObfuscation.Off,
+ selectedObfuscationPort: Constraint<Port> = Constraint.Any,
quantumResistant: QuantumResistantState = QuantumResistantState.Off,
selectedWireguardPort: Constraint<Port> = Constraint.Any,
customWireguardPort: Constraint.Only<Port>? = null,
@@ -47,6 +50,7 @@ data class VpnSettingsUiState(
customDnsItems,
contentBlockersOptions,
selectedObfuscation,
+ selectedObfuscationPort,
quantumResistant,
selectedWireguardPort,
customWireguardPort,
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/test/ComposeTestTagConstants.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/test/ComposeTestTagConstants.kt
index 8ebdaede33..0111fc7a46 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/test/ComposeTestTagConstants.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/test/ComposeTestTagConstants.kt
@@ -9,11 +9,15 @@ const val LAZY_LIST_TEST_TAG = "lazy_list_test_tag"
const val LAZY_LIST_LAST_ITEM_TEST_TAG = "lazy_list_last_item_test_tag"
const val LAZY_LIST_QUANTUM_ITEM_OFF_TEST_TAG = "lazy_list_quantum_item_off_test_tag"
const val LAZY_LIST_QUANTUM_ITEM_ON_TEST_TAG = "lazy_list_quantum_item_on_test_tag"
-const val LAZY_LIST_WIREGUARD_PORT_ITEM_X_TEST_TAG = "lazy_list_quantum_item_%d_test_tag"
+const val LAZY_LIST_WIREGUARD_PORT_ITEM_X_TEST_TAG = "lazy_list_wireguard_item_%d_test_tag"
const val LAZY_LIST_WIREGUARD_CUSTOM_PORT_TEXT_TEST_TAG =
"lazy_list_wireguard_custom_port_text_test_tag"
const val LAZY_LIST_WIREGUARD_CUSTOM_PORT_NUMBER_TEST_TAG =
"lazy_list_wireguard_custom_port_number_test_tag"
+const val LAZY_LIST_UDP_OVER_TCP_PORT_TEST_TAG = "lazy_list_udp_over_tcp_port_test_tag"
+const val LAZY_LIST_UDP_OVER_TCP_PORT_ITEM_AUTOMATIC_TEST_TAG =
+ "lazy_list_udp_over_tcp_item_automatic_test_tag"
+const val LAZY_LIST_UDP_OVER_TCP_PORT_ITEM_X_TEST_TAG = "lazy_list_udp_over_tcp_item_%d_test_tag"
const val CUSTOM_PORT_DIALOG_INPUT_TEST_TAG = "custom_port_dialog_input_test_tag"
// SelectLocationScreen, ConnectScreen, CustomListLocationsScreen
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/constant/WireguardConstant.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/constant/WireguardConstant.kt
index b4ed1e29a9..6f6cb5a79b 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/constant/WireguardConstant.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/constant/WireguardConstant.kt
@@ -1,3 +1,4 @@
package net.mullvad.mullvadvpn.constant
val WIREGUARD_PRESET_PORTS = listOf(51820, 53)
+val UDP2TCP_PRESET_PORTS = listOf(80, 5001)
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/repository/SettingsRepository.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/repository/SettingsRepository.kt
index b721442197..7a9be0303a 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/repository/SettingsRepository.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/repository/SettingsRepository.kt
@@ -8,13 +8,15 @@ import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.stateIn
import net.mullvad.mullvadvpn.lib.daemon.grpc.ManagementService
+import net.mullvad.mullvadvpn.lib.model.Constraint
import net.mullvad.mullvadvpn.lib.model.CustomDnsOptions
import net.mullvad.mullvadvpn.lib.model.DefaultDnsOptions
import net.mullvad.mullvadvpn.lib.model.DnsOptions
import net.mullvad.mullvadvpn.lib.model.DnsState
import net.mullvad.mullvadvpn.lib.model.Mtu
-import net.mullvad.mullvadvpn.lib.model.ObfuscationSettings
+import net.mullvad.mullvadvpn.lib.model.Port
import net.mullvad.mullvadvpn.lib.model.QuantumResistantState
+import net.mullvad.mullvadvpn.lib.model.SelectedObfuscation
import net.mullvad.mullvadvpn.lib.model.Settings
class SettingsRepository(
@@ -52,6 +54,9 @@ class SettingsRepository(
suspend fun addCustomDns(address: InetAddress) = managementService.addCustomDns(address)
+ suspend fun setCustomObfuscationPort(constraint: Constraint<Port>) =
+ managementService.setObfuscationPort(constraint)
+
suspend fun setWireguardMtu(mtu: Mtu) = managementService.setWireguardMtu(mtu.value)
suspend fun resetWireguardMtu() = managementService.resetWireguardMtu()
@@ -59,8 +64,7 @@ class SettingsRepository(
suspend fun setWireguardQuantumResistant(value: QuantumResistantState) =
managementService.setWireguardQuantumResistant(value)
- suspend fun setObfuscationOptions(value: ObfuscationSettings) =
- managementService.setObfuscationOptions(value)
+ suspend fun setObfuscation(value: SelectedObfuscation) = managementService.setObfuscation(value)
suspend fun setAutoConnect(isEnabled: Boolean) = managementService.setAutoConnect(isEnabled)
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/VpnSettingsViewModel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/VpnSettingsViewModel.kt
index 864d402fb3..a0a38ef6f8 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/VpnSettingsViewModel.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/VpnSettingsViewModel.kt
@@ -22,12 +22,10 @@ import net.mullvad.mullvadvpn.compose.state.VpnSettingsUiState
import net.mullvad.mullvadvpn.lib.model.Constraint
import net.mullvad.mullvadvpn.lib.model.DefaultDnsOptions
import net.mullvad.mullvadvpn.lib.model.DnsState
-import net.mullvad.mullvadvpn.lib.model.ObfuscationSettings
import net.mullvad.mullvadvpn.lib.model.Port
import net.mullvad.mullvadvpn.lib.model.QuantumResistantState
import net.mullvad.mullvadvpn.lib.model.SelectedObfuscation
import net.mullvad.mullvadvpn.lib.model.Settings
-import net.mullvad.mullvadvpn.lib.model.Udp2TcpObfuscationSettings
import net.mullvad.mullvadvpn.lib.model.WireguardConstraints
import net.mullvad.mullvadvpn.repository.RelayListRepository
import net.mullvad.mullvadvpn.repository.SettingsRepository
@@ -57,10 +55,11 @@ class VpnSettingsViewModel(
private val customPort = MutableStateFlow<Constraint<Port>?>(null)
private val vmState =
- combine(repository.settingsUpdates, relayListRepository.portRanges, customPort) {
- settings,
- portRanges,
- customWgPort ->
+ combine(
+ repository.settingsUpdates,
+ relayListRepository.portRanges,
+ customPort,
+ ) { settings, portRanges, customWgPort ->
VpnSettingsViewModelState(
mtuValue = settings?.tunnelOptions?.wireguard?.mtu,
isAutoConnectEnabled = settings?.autoConnect ?: false,
@@ -71,6 +70,8 @@ class VpnSettingsViewModel(
settings?.contentBlockersSettings() ?: DefaultDnsOptions(),
selectedObfuscation =
settings?.selectedObfuscationSettings() ?: SelectedObfuscation.Off,
+ selectedObfuscationPort =
+ settings?.obfuscationSettings?.udp2tcp?.port ?: Constraint.Any,
quantumResistant = settings?.quantumResistant() ?: QuantumResistantState.Off,
selectedWireguardPort = settings?.getWireguardPort() ?: Constraint.Any,
customWireguardPort = customWgPort,
@@ -203,17 +204,16 @@ class VpnSettingsViewModel(
fun onSelectObfuscationSetting(selectedObfuscation: SelectedObfuscation) {
viewModelScope.launch(dispatcher) {
- repository
- .setObfuscationOptions(
- ObfuscationSettings(
- selectedObfuscation = selectedObfuscation,
- udp2tcp = Udp2TcpObfuscationSettings(Constraint.Any)
- )
- )
- .onLeft { _uiSideEffect.send(VpnSettingsSideEffect.ShowToast.GenericError) }
+ repository.setObfuscation(selectedObfuscation).onLeft {
+ _uiSideEffect.send(VpnSettingsSideEffect.ShowToast.GenericError)
+ }
}
}
+ fun onObfuscationPortSelected(port: Constraint<Port>) {
+ viewModelScope.launch { repository.setCustomObfuscationPort(port) }
+ }
+
fun onSelectQuantumResistanceSetting(quantumResistant: QuantumResistantState) {
viewModelScope.launch(dispatcher) {
repository.setWireguardQuantumResistant(quantumResistant).onLeft {
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/VpnSettingsViewModelState.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/VpnSettingsViewModelState.kt
index f8e4f0b799..d8be8d1cf2 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/VpnSettingsViewModelState.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/VpnSettingsViewModelState.kt
@@ -17,6 +17,7 @@ data class VpnSettingsViewModelState(
val customDnsList: List<CustomDnsItem>,
val contentBlockersOptions: DefaultDnsOptions,
val selectedObfuscation: SelectedObfuscation,
+ val selectedObfuscationPort: Constraint<Port>,
val quantumResistant: QuantumResistantState,
val selectedWireguardPort: Constraint<Port>,
val customWireguardPort: Constraint<Port>?,
@@ -32,6 +33,7 @@ data class VpnSettingsViewModelState(
customDnsList,
contentBlockersOptions,
selectedObfuscation,
+ selectedObfuscationPort,
quantumResistant,
selectedWireguardPort,
customWireguardPort,
@@ -49,6 +51,7 @@ data class VpnSettingsViewModelState(
customDnsList = listOf(),
contentBlockersOptions = DefaultDnsOptions(),
selectedObfuscation = SelectedObfuscation.Auto,
+ selectedObfuscationPort = Constraint.Any,
quantumResistant = QuantumResistantState.Off,
selectedWireguardPort = Constraint.Any,
customWireguardPort = null,
diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/VpnSettingsViewModelTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/VpnSettingsViewModelTest.kt
index 29a6c764ba..a62b5d4bb3 100644
--- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/VpnSettingsViewModelTest.kt
+++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/VpnSettingsViewModelTest.kt
@@ -67,6 +67,19 @@ class VpnSettingsViewModelTest {
}
@Test
+ fun `onSelectCustomTcpOverUdpPort should invoke setCustomObfuscationPort on SettingsRepository`() =
+ runTest {
+ val customPort = Port(5001)
+ coEvery {
+ mockSettingsRepository.setCustomObfuscationPort(Constraint.Only(customPort))
+ } returns Unit.right()
+ viewModel.onObfuscationPortSelected(Constraint.Only(customPort))
+ coVerify(exactly = 1) {
+ mockSettingsRepository.setCustomObfuscationPort(Constraint.Only(customPort))
+ }
+ }
+
+ @Test
fun `onSelectQuantumResistanceSetting should invoke setWireguardQuantumResistant on SettingsRepository`() =
runTest {
val quantumResistantState = QuantumResistantState.On