diff options
8 files changed, 91 insertions, 17 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 15b0d935b2..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 @@ -27,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 @@ -209,15 +211,18 @@ class VpnSettingsScreenTest { } @Test - fun testShowTcpOverUdpPortOptions() = + 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 ) } @@ -236,6 +241,44 @@ class VpnSettingsScreenTest { 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 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 3332aa264e..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 @@ -594,6 +594,7 @@ fun VpnSettingsScreen( 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 @@ -605,6 +606,7 @@ fun VpnSettingsScreen( 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, ) @@ -615,6 +617,7 @@ fun VpnSettingsScreen( SelectableCell( title = port.toString(), isSelected = state.selectedObfuscationPort.hasValue(port), + isEnabled = state.selectObfuscationPortEnabled, onCellClicked = { onObfuscationPortSelected(Constraint.Only(Port(port))) }, 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 cefc5edb7a..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 @@ -24,6 +24,7 @@ data class VpnSettingsUiState( val availablePortRanges: List<PortRange>, val systemVpnSettingsAvailable: Boolean, ) { + val selectObfuscationPortEnabled = selectedObfuscation != SelectedObfuscation.Off companion object { fun createDefault( 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 a1a1ed007a..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 @@ -14,9 +14,9 @@ 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( @@ -64,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 0ad91af8cc..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 @@ -206,14 +204,9 @@ 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) + } } } diff --git a/android/lib/daemon-grpc/src/main/kotlin/net/mullvad/mullvadvpn/lib/daemon/grpc/ManagementService.kt b/android/lib/daemon-grpc/src/main/kotlin/net/mullvad/mullvadvpn/lib/daemon/grpc/ManagementService.kt index 268f664840..4c4bdc623a 100644 --- a/android/lib/daemon-grpc/src/main/kotlin/net/mullvad/mullvadvpn/lib/daemon/grpc/ManagementService.kt +++ b/android/lib/daemon-grpc/src/main/kotlin/net/mullvad/mullvadvpn/lib/daemon/grpc/ManagementService.kt @@ -87,6 +87,7 @@ import net.mullvad.mullvadvpn.lib.model.RelayList as ModelRelayList import net.mullvad.mullvadvpn.lib.model.RelayList import net.mullvad.mullvadvpn.lib.model.RelaySettings import net.mullvad.mullvadvpn.lib.model.RemoveSplitTunnelingAppError +import net.mullvad.mullvadvpn.lib.model.SelectedObfuscation import net.mullvad.mullvadvpn.lib.model.SetAllowLanError import net.mullvad.mullvadvpn.lib.model.SetAutoConnectError import net.mullvad.mullvadvpn.lib.model.SetDnsOptionsError @@ -109,6 +110,7 @@ import net.mullvad.mullvadvpn.lib.model.location import net.mullvad.mullvadvpn.lib.model.ownership import net.mullvad.mullvadvpn.lib.model.providers import net.mullvad.mullvadvpn.lib.model.relayConstraints +import net.mullvad.mullvadvpn.lib.model.selectedObfuscation import net.mullvad.mullvadvpn.lib.model.state import net.mullvad.mullvadvpn.lib.model.udp2tcp import net.mullvad.mullvadvpn.lib.model.wireguardConstraints @@ -390,6 +392,21 @@ class ManagementService( .mapLeft(SetObfuscationOptionsError::Unknown) .mapEmpty() + suspend fun setObfuscation( + value: SelectedObfuscation + ): Either<SetObfuscationOptionsError, Unit> = + Either.catch { + val updatedObfuscationSettings = + ObfuscationSettings.selectedObfuscation.modify( + getSettings().obfuscationSettings + ) { + value + } + grpc.setObfuscationSettings(updatedObfuscationSettings.fromDomain()) + } + .mapLeft(SetObfuscationOptionsError::Unknown) + .mapEmpty() + suspend fun setObfuscationPort( portConstraint: Constraint<Port> ): Either<SetObfuscationOptionsError, Unit> = either { |
