diff options
| author | Jonatan Rhodin <jonatan.rhodin@mullvad.net> | 2023-06-21 14:15:56 +0200 |
|---|---|---|
| committer | Jonatan Rhodin <jonatan.rhodin@mullvad.net> | 2023-07-07 11:12:15 +0200 |
| commit | 9f313d321dd0361545e71688bde320987eed8d48 (patch) | |
| tree | 944fd328f24a5072da6a4512508c90991bdef24b /android | |
| parent | afb24305d9da874b0f20802484411a7879a3d7ca (diff) | |
| download | mullvadvpn-9f313d321dd0361545e71688bde320987eed8d48.tar.xz mullvadvpn-9f313d321dd0361545e71688bde320987eed8d48.zip | |
Add option to change wireguard port to vpn settings
Diffstat (limited to 'android')
34 files changed, 465 insertions, 50 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 e66e779313..fd3be6092f 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 @@ -21,6 +21,10 @@ 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_WIREGUARD_PORT_ITEM_X_TEST_TAG +import net.mullvad.mullvadvpn.model.Constraint +import net.mullvad.mullvadvpn.model.Port +import net.mullvad.mullvadvpn.model.PortRange import net.mullvad.mullvadvpn.model.QuantumResistantState import net.mullvad.mullvadvpn.onNodeWithTagAndChildrenText import net.mullvad.mullvadvpn.viewmodel.CustomDnsItem @@ -46,14 +50,18 @@ class VpnSettingsScreenTest { toastMessagesSharedFlow = MutableSharedFlow<String>().asSharedFlow() ) } + + composeTestRule.apply { + onNodeWithText("WireGuard MTU").assertExists() + onNodeWithText("Default").assertExists() + } + composeTestRule .onNodeWithTag(LAZY_LIST_TEST_TAG) .performScrollToNode(hasTestTag(LAZY_LIST_LAST_ITEM_TEST_TAG)) // Assert composeTestRule.apply { - onNodeWithText("WireGuard MTU").assertExists() - onNodeWithText("Default").assertExists() onNodeWithText("Use custom DNS server").assertExists() onNodeWithText("Add a server").assertDoesNotExist() } @@ -603,6 +611,87 @@ class VpnSettingsScreenTest { composeTestRule.onNodeWithText("Got it!").assertExists() } + @Test + fun testShowWireguardPortOptions() { + // Arrange + composeTestRule.setContent { + VpnSettingsScreen( + uiState = + VpnSettingsUiState.DefaultUiState( + selectedWireguardPort = Constraint.Only(Port(53)) + ), + toastMessagesSharedFlow = MutableSharedFlow<String>().asSharedFlow() + ) + } + composeTestRule + .onNodeWithTag(LAZY_LIST_TEST_TAG) + .performScrollToNode( + hasTestTag(String.format(LAZY_LIST_WIREGUARD_PORT_ITEM_X_TEST_TAG, 53)) + ) + + // Assert + composeTestRule + .onNodeWithTagAndChildrenText( + testTag = String.format(LAZY_LIST_WIREGUARD_PORT_ITEM_X_TEST_TAG, 51820), + text = "51820" + ) + .assertExists() + } + + @Test + fun testSelectWireguardPortOption() { + // Arrange + val mockSelectWireguardPortSelectionListener: (Constraint<Port>) -> Unit = + mockk(relaxed = true) + composeTestRule.setContent { + VpnSettingsScreen( + uiState = + VpnSettingsUiState.DefaultUiState( + selectedWireguardPort = Constraint.Only(Port(53)) + ), + onWireguardPortSelected = mockSelectWireguardPortSelectionListener, + toastMessagesSharedFlow = MutableSharedFlow<String>().asSharedFlow() + ) + } + composeTestRule + .onNodeWithTag(LAZY_LIST_TEST_TAG) + .performScrollToNode( + hasTestTag(String.format(LAZY_LIST_WIREGUARD_PORT_ITEM_X_TEST_TAG, 53)) + ) + + // Assert + composeTestRule + .onNodeWithTagAndChildrenText( + testTag = String.format(LAZY_LIST_WIREGUARD_PORT_ITEM_X_TEST_TAG, 51820), + text = "51820" + ) + .performClick() + verify(exactly = 1) { + mockSelectWireguardPortSelectionListener.invoke(Constraint.Only(Port(51820))) + } + } + + @Test + fun testShowWireguardPortInfo() { + // Arrange + composeTestRule.setContent { + VpnSettingsScreen( + uiState = + VpnSettingsUiState.WireguardPortInfoDialogUiState( + availablePortRanges = listOf(PortRange(53, 53), PortRange(120, 121)) + ), + toastMessagesSharedFlow = MutableSharedFlow<String>().asSharedFlow() + ) + } + + // Assert + composeTestRule + .onNodeWithText( + "The automatic setting will randomly choose from the valid port ranges shown below." + ) + .assertExists() + } + companion object { private const val LOCAL_DNS_SERVER_WARNING = "The local DNS server will not work unless you enable " + diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/BaseCell.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/BaseCell.kt index 6025abb281..e8c0518233 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/BaseCell.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/BaseCell.kt @@ -115,10 +115,15 @@ internal fun BaseCell( } @Composable -internal fun BaseCellTitle(title: String, style: TextStyle, modifier: Modifier = Modifier) { +internal fun BaseCellTitle( + title: String, + style: TextStyle, + modifier: Modifier = Modifier, + textAlign: TextAlign = TextAlign.Center +) { Text( text = title, - textAlign = TextAlign.Center, + textAlign = textAlign, style = style, color = MaterialTheme.colorScheme.onPrimary, modifier = modifier.wrapContentWidth(align = Alignment.End).wrapContentHeight() diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/WireguardPortInfoDialog.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/WireguardPortInfoDialog.kt new file mode 100644 index 0000000000..680b395898 --- /dev/null +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/WireguardPortInfoDialog.kt @@ -0,0 +1,28 @@ +package net.mullvad.mullvadvpn.compose.dialog + +import androidx.compose.runtime.Composable +import androidx.compose.ui.res.stringResource +import net.mullvad.mullvadvpn.R +import net.mullvad.mullvadvpn.model.PortRange + +@Composable +fun WireguardPortInfoDialog(portRanges: List<PortRange>, onDismiss: () -> Unit) { + InfoDialog( + message = stringResource(id = R.string.wireguard_port_info_description), + additionalInfo = + buildString { + portRanges.forEachIndexed { index, range -> + if (index != 0) { + append(",") + append(" ") + } + if (range.from == range.to) { + append(range.from) + } else { + append("${range.from}-${range.to}") + } + } + }, + onDismiss = onDismiss + ) +} 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 bc44670940..f5c8d39cd4 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 @@ -64,16 +64,22 @@ import net.mullvad.mullvadvpn.compose.dialog.MalwareInfoDialog import net.mullvad.mullvadvpn.compose.dialog.MtuDialog import net.mullvad.mullvadvpn.compose.dialog.ObfuscationInfoDialog import net.mullvad.mullvadvpn.compose.dialog.QuantumResistanceInfoDialog +import net.mullvad.mullvadvpn.compose.dialog.WireguardPortInfoDialog import net.mullvad.mullvadvpn.compose.extensions.itemWithDivider import net.mullvad.mullvadvpn.compose.state.VpnSettingsUiState 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_WIREGUARD_PORT_ITEM_X_TEST_TAG import net.mullvad.mullvadvpn.compose.theme.AppTheme import net.mullvad.mullvadvpn.compose.theme.Dimens +import net.mullvad.mullvadvpn.constant.WIREGUARD_PRESET_PORTS +import net.mullvad.mullvadvpn.model.Constraint +import net.mullvad.mullvadvpn.model.Port import net.mullvad.mullvadvpn.model.QuantumResistantState import net.mullvad.mullvadvpn.model.SelectedObfuscation +import net.mullvad.mullvadvpn.util.hasValue import net.mullvad.mullvadvpn.viewmodel.CustomDnsItem @Preview @@ -117,7 +123,9 @@ private fun PreviewVpnSettings() { onSelectObfuscationSetting = {}, onObfuscationInfoClick = {}, onSelectQuantumResistanceSetting = {}, - onQuantumResistanceInfoClicked = {} + onQuantumResistanceInfoClicked = {}, + onWireguardPortSelected = {}, + onWireguardPortInfoClicked = {} ) } } @@ -156,7 +164,9 @@ fun VpnSettingsScreen( onSelectObfuscationSetting: (selectedObfuscation: SelectedObfuscation) -> Unit = {}, onObfuscationInfoClick: () -> Unit = {}, onSelectQuantumResistanceSetting: (quantumResistant: QuantumResistantState) -> Unit = {}, - onQuantumResistanceInfoClicked: () -> Unit = {} + onQuantumResistanceInfoClicked: () -> Unit = {}, + onWireguardPortSelected: (port: Constraint<Port>) -> Unit = {}, + onWireguardPortInfoClicked: () -> Unit = {} ) { val cellVerticalSpacing = dimensionResource(id = R.dimen.cell_label_vertical_padding) val cellHorizontalSpacing = dimensionResource(id = R.dimen.cell_left_padding) @@ -199,6 +209,9 @@ fun VpnSettingsScreen( is VpnSettingsUiState.QuantumResistanceInfoDialogUiState -> { QuantumResistanceInfoDialog(onDismissInfoClick) } + is VpnSettingsUiState.WireguardPortInfoDialogUiState -> { + WireguardPortInfoDialog(uiState.availablePortRanges, onDismissInfoClick) + } else -> { // NOOP } @@ -420,6 +433,33 @@ fun VpnSettingsScreen( ) } + itemWithDivider { + Spacer(modifier = Modifier.height(cellVerticalSpacing)) + InformationComposeCell( + title = stringResource(id = R.string.wireguard_port_title), + onInfoClicked = onWireguardPortInfoClicked + ) + } + + itemWithDivider { + SelectableCell( + title = stringResource(id = R.string.automatic), + isSelected = uiState.selectedWireguardPort is Constraint.Any, + onCellClicked = { onWireguardPortSelected(Constraint.Any()) } + ) + } + + WIREGUARD_PRESET_PORTS.forEach { port -> + itemWithDivider { + SelectableCell( + title = port.toString(), + testTag = String.format(LAZY_LIST_WIREGUARD_PORT_ITEM_X_TEST_TAG, port), + isSelected = uiState.selectedWireguardPort.hasValue(port), + onCellClicked = { onWireguardPortSelected(Constraint.Only(Port(port))) } + ) + } + } + item { Spacer(modifier = Modifier.height(cellVerticalSpacing)) HeaderSwitchComposeCell( 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 1515b2d19a..0413e52ed1 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 @@ -1,6 +1,9 @@ package net.mullvad.mullvadvpn.compose.state +import net.mullvad.mullvadvpn.model.Constraint import net.mullvad.mullvadvpn.model.DefaultDnsOptions +import net.mullvad.mullvadvpn.model.Port +import net.mullvad.mullvadvpn.model.PortRange import net.mullvad.mullvadvpn.model.QuantumResistantState import net.mullvad.mullvadvpn.model.SelectedObfuscation import net.mullvad.mullvadvpn.viewmodel.CustomDnsItem @@ -16,6 +19,7 @@ sealed interface VpnSettingsUiState { val isAllowLanEnabled: Boolean val selectedObfuscation: SelectedObfuscation val quantumResistant: QuantumResistantState + val selectedWireguardPort: Constraint<Port> data class DefaultUiState( override val mtu: String = "", @@ -26,7 +30,8 @@ sealed interface VpnSettingsUiState { override val customDnsItems: List<CustomDnsItem> = listOf(), override val contentBlockersOptions: DefaultDnsOptions = DefaultDnsOptions(), override val selectedObfuscation: SelectedObfuscation = SelectedObfuscation.Off, - override val quantumResistant: QuantumResistantState = QuantumResistantState.Off + override val quantumResistant: QuantumResistantState = QuantumResistantState.Off, + override val selectedWireguardPort: Constraint<Port> = Constraint.Any() ) : VpnSettingsUiState data class MtuDialogUiState( @@ -39,7 +44,8 @@ sealed interface VpnSettingsUiState { override val contentBlockersOptions: DefaultDnsOptions = DefaultDnsOptions(), val mtuEditValue: String, override val selectedObfuscation: SelectedObfuscation = SelectedObfuscation.Off, - override val quantumResistant: QuantumResistantState = QuantumResistantState.Off + override val quantumResistant: QuantumResistantState = QuantumResistantState.Off, + override val selectedWireguardPort: Constraint<Port> = Constraint.Any() ) : VpnSettingsUiState data class DnsDialogUiState( @@ -52,7 +58,8 @@ sealed interface VpnSettingsUiState { override val contentBlockersOptions: DefaultDnsOptions = DefaultDnsOptions(), val stagedDns: StagedDns, override val selectedObfuscation: SelectedObfuscation = SelectedObfuscation.Off, - override val quantumResistant: QuantumResistantState = QuantumResistantState.Off + override val quantumResistant: QuantumResistantState = QuantumResistantState.Off, + override val selectedWireguardPort: Constraint<Port> = Constraint.Any() ) : VpnSettingsUiState data class LocalNetworkSharingInfoDialogUiState( @@ -64,7 +71,8 @@ sealed interface VpnSettingsUiState { override val customDnsItems: List<CustomDnsItem> = listOf(), override val contentBlockersOptions: DefaultDnsOptions = DefaultDnsOptions(), override val selectedObfuscation: SelectedObfuscation = SelectedObfuscation.Off, - override val quantumResistant: QuantumResistantState = QuantumResistantState.Off + override val quantumResistant: QuantumResistantState = QuantumResistantState.Off, + override val selectedWireguardPort: Constraint<Port> = Constraint.Any() ) : VpnSettingsUiState data class ContentBlockersInfoDialogUiState( @@ -76,7 +84,8 @@ sealed interface VpnSettingsUiState { override val customDnsItems: List<CustomDnsItem> = listOf(), override val contentBlockersOptions: DefaultDnsOptions = DefaultDnsOptions(), override val selectedObfuscation: SelectedObfuscation = SelectedObfuscation.Off, - override val quantumResistant: QuantumResistantState = QuantumResistantState.Off + override val quantumResistant: QuantumResistantState = QuantumResistantState.Off, + override val selectedWireguardPort: Constraint<Port> = Constraint.Any() ) : VpnSettingsUiState data class CustomDnsInfoDialogUiState( @@ -88,7 +97,8 @@ sealed interface VpnSettingsUiState { override val customDnsItems: List<CustomDnsItem> = listOf(), override val contentBlockersOptions: DefaultDnsOptions = DefaultDnsOptions(), override val selectedObfuscation: SelectedObfuscation = SelectedObfuscation.Off, - override val quantumResistant: QuantumResistantState = QuantumResistantState.Off + override val quantumResistant: QuantumResistantState = QuantumResistantState.Off, + override val selectedWireguardPort: Constraint<Port> = Constraint.Any() ) : VpnSettingsUiState data class MalwareInfoDialogUiState( @@ -100,7 +110,8 @@ sealed interface VpnSettingsUiState { override val customDnsItems: List<CustomDnsItem> = listOf(), override val contentBlockersOptions: DefaultDnsOptions = DefaultDnsOptions(), override val selectedObfuscation: SelectedObfuscation = SelectedObfuscation.Off, - override val quantumResistant: QuantumResistantState = QuantumResistantState.Off + override val quantumResistant: QuantumResistantState = QuantumResistantState.Off, + override val selectedWireguardPort: Constraint<Port> = Constraint.Any() ) : VpnSettingsUiState data class ObfuscationInfoDialogUiState( @@ -112,7 +123,8 @@ sealed interface VpnSettingsUiState { override val customDnsItems: List<CustomDnsItem> = listOf(), override val contentBlockersOptions: DefaultDnsOptions = DefaultDnsOptions(), override val selectedObfuscation: SelectedObfuscation = SelectedObfuscation.Off, - override val quantumResistant: QuantumResistantState = QuantumResistantState.Off + override val quantumResistant: QuantumResistantState = QuantumResistantState.Off, + override val selectedWireguardPort: Constraint<Port> = Constraint.Any(), ) : VpnSettingsUiState data class QuantumResistanceInfoDialogUiState( @@ -124,6 +136,21 @@ sealed interface VpnSettingsUiState { override val customDnsItems: List<CustomDnsItem> = listOf(), override val contentBlockersOptions: DefaultDnsOptions = DefaultDnsOptions(), override val selectedObfuscation: SelectedObfuscation = SelectedObfuscation.Off, - override val quantumResistant: QuantumResistantState = QuantumResistantState.Off + override val quantumResistant: QuantumResistantState = QuantumResistantState.Off, + override val selectedWireguardPort: Constraint<Port> = Constraint.Any() + ) : VpnSettingsUiState + + data class WireguardPortInfoDialogUiState( + override val mtu: String = "", + override val isAutoConnectEnabled: Boolean = false, + override val isLocalNetworkSharingEnabled: Boolean = false, + override val isCustomDnsEnabled: Boolean = false, + override val isAllowLanEnabled: Boolean = false, + override val customDnsItems: List<CustomDnsItem> = listOf(), + override val contentBlockersOptions: DefaultDnsOptions = DefaultDnsOptions(), + override val selectedObfuscation: SelectedObfuscation = SelectedObfuscation.Off, + override val quantumResistant: QuantumResistantState = QuantumResistantState.Off, + override val selectedWireguardPort: Constraint<Port> = Constraint.Any(), + val availablePortRanges: List<PortRange> = emptyList() ) : VpnSettingsUiState } 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 326908667c..84b9e32afe 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 @@ -5,6 +5,7 @@ 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" // SelectLocationScreen const val CIRCULAR_PROGRESS_INDICATOR = "circular_progress_indicator" 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 new file mode 100644 index 0000000000..b4ed1e29a9 --- /dev/null +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/constant/WireguardConstant.kt @@ -0,0 +1,3 @@ +package net.mullvad.mullvadvpn.constant + +val WIREGUARD_PRESET_PORTS = listOf(51820, 53) diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/di/UiModule.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/di/UiModule.kt index bbe632f9d5..2244438ef0 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/di/UiModule.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/di/UiModule.kt @@ -85,13 +85,7 @@ val uiModule = module { ChangelogViewModel(get(), BuildConfig.VERSION_CODE, BuildConfig.ALWAYS_SHOW_CHANGELOG) } viewModel { PrivacyDisclaimerViewModel(get()) } - viewModel { - VpnSettingsViewModel( - get(), - get(), - get(), - ) - } + viewModel { VpnSettingsViewModel(get(), get(), get(), get()) } viewModel { SelectLocationViewModel(get()) } } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/VpnSettingsFragment.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/VpnSettingsFragment.kt index f90186e548..45c7f4b14e 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/VpnSettingsFragment.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/VpnSettingsFragment.kt @@ -55,7 +55,9 @@ class VpnSettingsFragment : BaseFragment() { onSelectObfuscationSetting = vm::onSelectObfuscationSetting, onObfuscationInfoClick = vm::onObfuscationInfoClick, onSelectQuantumResistanceSetting = vm::onSelectQuantumResistanceSetting, - onQuantumResistanceInfoClicked = vm::onQuantumResistanceInfoClicked + onQuantumResistanceInfoClicked = vm::onQuantumResistanceInfoClicked, + onWireguardPortSelected = vm::onWireguardPortSelected, + onWireguardPortInfoClicked = vm::onWireguardPortInfoClicked ) } } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/RelayListListener.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/RelayListListener.kt index c6142694b9..eb31a264ac 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/RelayListListener.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/RelayListListener.kt @@ -73,7 +73,7 @@ class RelayListListener( eventDispatcher.registerHandler(Event.NewRelayList::class) { event -> event.relayList?.let { relayLocations -> relayListChanged(RelayList(relayLocations)) - onPortRangesChange?.invoke(relayLocations.wireguardEndpointData.portRanges) + portRangesChanged(relayLocations.wireguardEndpointData.portRanges) } } @@ -90,6 +90,7 @@ class RelayListListener( private fun relaySettingsChanged(newRelaySettings: RelaySettings?) { synchronized(this) { val relayList = this.relayList + val portRanges = this.portRanges relaySettings = newRelaySettings @@ -100,6 +101,7 @@ class RelayListListener( if (relayList != null) { relayListChanged(relayList) } + portRangesChanged(portRanges) } } @@ -112,6 +114,14 @@ class RelayListListener( } } + private fun portRangesChanged(newPortRanges: List<PortRange>) { + synchronized(this) { + portRanges = newPortRanges + + onPortRangesChange?.invoke(portRanges) + } + } + private fun findSelectedRelayItem(): RelayItem? { val relaySettings = this.relaySettings diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/ConstraintExtensions.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/ConstraintExtensions.kt new file mode 100644 index 0000000000..9be01b2952 --- /dev/null +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/ConstraintExtensions.kt @@ -0,0 +1,10 @@ +package net.mullvad.mullvadvpn.util + +import net.mullvad.mullvadvpn.model.Constraint +import net.mullvad.mullvadvpn.model.Port + +fun Constraint<Port>.hasValue(value: Int) = + when (this) { + is Constraint.Any -> false + is Constraint.Only -> this.value.value == value + } 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 81811722c7..a5fa4721bd 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 @@ -7,11 +7,16 @@ import androidx.lifecycle.viewModelScope import java.net.InetAddress import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.asSharedFlow +import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.emptyFlow +import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.update @@ -22,11 +27,18 @@ import net.mullvad.mullvadvpn.model.Constraint import net.mullvad.mullvadvpn.model.DefaultDnsOptions import net.mullvad.mullvadvpn.model.DnsState import net.mullvad.mullvadvpn.model.ObfuscationSettings +import net.mullvad.mullvadvpn.model.Port import net.mullvad.mullvadvpn.model.QuantumResistantState +import net.mullvad.mullvadvpn.model.RelaySettings import net.mullvad.mullvadvpn.model.SelectedObfuscation import net.mullvad.mullvadvpn.model.Settings import net.mullvad.mullvadvpn.model.Udp2TcpObfuscationSettings +import net.mullvad.mullvadvpn.model.WireguardConstraints import net.mullvad.mullvadvpn.repository.SettingsRepository +import net.mullvad.mullvadvpn.ui.serviceconnection.RelayListListener +import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnectionManager +import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnectionState +import net.mullvad.mullvadvpn.ui.serviceconnection.relayListListener import net.mullvad.mullvadvpn.util.isValidMtu import org.apache.commons.validator.routines.InetAddressValidator @@ -34,6 +46,7 @@ class VpnSettingsViewModel( private val repository: SettingsRepository, private val inetAddressValidator: InetAddressValidator, private val resources: Resources, + private val serviceConnectionManager: ServiceConnectionManager, private val dispatcher: CoroutineDispatcher = Dispatchers.IO ) : ViewModel() { @@ -44,21 +57,38 @@ class VpnSettingsViewModel( MutableStateFlow<VpnSettingsDialogState>(VpnSettingsDialogState.NoDialog) private val vmState = - combine(repository.settingsUpdates, dialogState) { settings, dialogState -> - VpnSettingsViewModelState( - mtuValue = settings?.mtuString() ?: "", - isAutoConnectEnabled = settings?.autoConnect ?: false, - isLocalNetworkSharingEnabled = settings?.allowLan ?: false, - isCustomDnsEnabled = settings?.isCustomDnsEnabled() ?: false, - customDnsList = settings?.addresses()?.asStringAddressList() ?: listOf(), - contentBlockersOptions = settings?.contentBlockersSettings() - ?: DefaultDnsOptions(), - isAllowLanEnabled = settings?.allowLan ?: false, - selectedObfuscation = settings?.selectedObfuscationSettings() - ?: SelectedObfuscation.Off, - dialogState = dialogState, - quantumResistant = settings?.quantumResistant() ?: QuantumResistantState.Off - ) + serviceConnectionManager.connectionState + .flatMapLatest { state -> + if (state is ServiceConnectionState.ConnectedReady) { + flowOf(state.container) + } else { + emptyFlow() + } + } + .flatMapLatest { serviceConnection -> + combine( + repository.settingsUpdates, + serviceConnection.relayListListener.portRangesCallbackFlow(), + dialogState + ) { settings, portRanges, dialogState -> + VpnSettingsViewModelState( + mtuValue = settings?.mtuString() ?: "", + isAutoConnectEnabled = settings?.autoConnect ?: false, + isLocalNetworkSharingEnabled = settings?.allowLan ?: false, + isCustomDnsEnabled = settings?.isCustomDnsEnabled() ?: false, + customDnsList = settings?.addresses()?.asStringAddressList() ?: listOf(), + contentBlockersOptions = settings?.contentBlockersSettings() + ?: DefaultDnsOptions(), + isAllowLanEnabled = settings?.allowLan ?: false, + selectedObfuscation = settings?.selectedObfuscationSettings() + ?: SelectedObfuscation.Off, + dialogState = dialogState, + quantumResistant = settings?.quantumResistant() + ?: QuantumResistantState.Off, + selectedWireguardPort = settings?.getWireguardPort() ?: Constraint.Any(), + availablePortRanges = portRanges + ) + } } .stateIn( viewModelScope, @@ -317,6 +347,17 @@ class VpnSettingsViewModel( dialogState.update { VpnSettingsDialogState.QuantumResistanceInfoDialog } } + fun onWireguardPortSelected(port: Constraint<Port>) { + viewModelScope.launch(dispatcher) { + serviceConnectionManager.relayListListener()?.selectedWireguardConstraints = + WireguardConstraints(port = port) + } + } + + fun onWireguardPortInfoClicked() { + dialogState.update { VpnSettingsDialogState.WireguardPortInfoDialog } + } + private fun updateDefaultDnsOptionsViaRepository(contentBlockersOption: DefaultDnsOptions) = viewModelScope.launch(dispatcher) { repository.setDnsOptions( @@ -363,6 +404,17 @@ class VpnSettingsViewModel( private fun Settings.selectedObfuscationSettings() = obfuscationSettings.selectedObfuscation + private fun RelayListListener.portRangesCallbackFlow() = callbackFlow { + onPortRangesChange = { portRanges -> this.trySend(portRanges) } + awaitClose { onPortRangesChange = null } + } + + private fun Settings.getWireguardPort() = + when (relaySettings) { + RelaySettings.CustomTunnelEndpoint -> Constraint.Any() + is RelaySettings.Normal -> relaySettings.relayConstraints.wireguardConstraints.port + } + private fun String.isValidIp(): Boolean { return inetAddressValidator.isValid(this) } 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 8cf675b786..b77b107037 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 @@ -1,7 +1,10 @@ package net.mullvad.mullvadvpn.viewmodel import net.mullvad.mullvadvpn.compose.state.VpnSettingsUiState +import net.mullvad.mullvadvpn.model.Constraint import net.mullvad.mullvadvpn.model.DefaultDnsOptions +import net.mullvad.mullvadvpn.model.Port +import net.mullvad.mullvadvpn.model.PortRange import net.mullvad.mullvadvpn.model.QuantumResistantState import net.mullvad.mullvadvpn.model.SelectedObfuscation @@ -15,7 +18,9 @@ data class VpnSettingsViewModelState( val contentBlockersOptions: DefaultDnsOptions, val selectedObfuscation: SelectedObfuscation, val dialogState: VpnSettingsDialogState, - val quantumResistant: QuantumResistantState + val quantumResistant: QuantumResistantState, + val selectedWireguardPort: Constraint<Port>, + val availablePortRanges: List<PortRange> ) { fun toUiState(): VpnSettingsUiState { return when (dialogState) { @@ -30,7 +35,8 @@ data class VpnSettingsViewModelState( contentBlockersOptions = contentBlockersOptions, mtuEditValue = dialogState.mtuEditValue, selectedObfuscation = selectedObfuscation, - quantumResistant = quantumResistant + quantumResistant = quantumResistant, + selectedWireguardPort = selectedWireguardPort ) is VpnSettingsDialogState.DnsDialog -> VpnSettingsUiState.DnsDialogUiState( @@ -43,7 +49,8 @@ data class VpnSettingsViewModelState( contentBlockersOptions = contentBlockersOptions, stagedDns = dialogState.stagedDns, selectedObfuscation = selectedObfuscation, - quantumResistant = quantumResistant + quantumResistant = quantumResistant, + selectedWireguardPort = selectedWireguardPort ) is VpnSettingsDialogState.LocalNetworkSharingInfoDialog -> VpnSettingsUiState.LocalNetworkSharingInfoDialogUiState( @@ -55,7 +62,8 @@ data class VpnSettingsViewModelState( customDnsItems = customDnsList, contentBlockersOptions = contentBlockersOptions, selectedObfuscation = selectedObfuscation, - quantumResistant = quantumResistant + quantumResistant = quantumResistant, + selectedWireguardPort = selectedWireguardPort ) is VpnSettingsDialogState.ContentBlockersInfoDialog -> VpnSettingsUiState.ContentBlockersInfoDialogUiState( @@ -67,7 +75,8 @@ data class VpnSettingsViewModelState( customDnsItems = customDnsList, contentBlockersOptions = contentBlockersOptions, selectedObfuscation = selectedObfuscation, - quantumResistant = quantumResistant + quantumResistant = quantumResistant, + selectedWireguardPort = selectedWireguardPort ) is VpnSettingsDialogState.CustomDnsInfoDialog -> VpnSettingsUiState.CustomDnsInfoDialogUiState( @@ -79,7 +88,8 @@ data class VpnSettingsViewModelState( customDnsItems = customDnsList, contentBlockersOptions = contentBlockersOptions, selectedObfuscation = selectedObfuscation, - quantumResistant = quantumResistant + quantumResistant = quantumResistant, + selectedWireguardPort = selectedWireguardPort ) is VpnSettingsDialogState.MalwareInfoDialog -> VpnSettingsUiState.MalwareInfoDialogUiState( @@ -91,7 +101,8 @@ data class VpnSettingsViewModelState( customDnsItems = customDnsList, contentBlockersOptions = contentBlockersOptions, selectedObfuscation = selectedObfuscation, - quantumResistant = quantumResistant + quantumResistant = quantumResistant, + selectedWireguardPort = selectedWireguardPort ) is VpnSettingsDialogState.ObfuscationInfoDialog -> VpnSettingsUiState.ObfuscationInfoDialogUiState( @@ -103,7 +114,8 @@ data class VpnSettingsViewModelState( customDnsItems = customDnsList, contentBlockersOptions = contentBlockersOptions, selectedObfuscation = selectedObfuscation, - quantumResistant = quantumResistant + quantumResistant = quantumResistant, + selectedWireguardPort = selectedWireguardPort ) is VpnSettingsDialogState.QuantumResistanceInfoDialog -> { VpnSettingsUiState.QuantumResistanceInfoDialogUiState( @@ -115,7 +127,23 @@ data class VpnSettingsViewModelState( customDnsItems = customDnsList, contentBlockersOptions = contentBlockersOptions, selectedObfuscation = selectedObfuscation, - quantumResistant = quantumResistant + quantumResistant = quantumResistant, + selectedWireguardPort = selectedWireguardPort + ) + } + is VpnSettingsDialogState.WireguardPortInfoDialog -> { + VpnSettingsUiState.WireguardPortInfoDialogUiState( + mtu = mtuValue, + isAutoConnectEnabled = isAutoConnectEnabled, + isLocalNetworkSharingEnabled = isLocalNetworkSharingEnabled, + isCustomDnsEnabled = isCustomDnsEnabled, + isAllowLanEnabled = isAllowLanEnabled, + customDnsItems = customDnsList, + contentBlockersOptions = contentBlockersOptions, + selectedObfuscation = selectedObfuscation, + quantumResistant = quantumResistant, + selectedWireguardPort = selectedWireguardPort, + availablePortRanges = availablePortRanges ) } else -> @@ -128,7 +156,8 @@ data class VpnSettingsViewModelState( customDnsItems = customDnsList, contentBlockersOptions = contentBlockersOptions, selectedObfuscation = selectedObfuscation, - quantumResistant = quantumResistant + quantumResistant = quantumResistant, + selectedWireguardPort = selectedWireguardPort ) } } @@ -147,7 +176,9 @@ data class VpnSettingsViewModelState( isAllowLanEnabled = false, dialogState = VpnSettingsDialogState.NoDialog, selectedObfuscation = SelectedObfuscation.Auto, - quantumResistant = QuantumResistantState.Off + quantumResistant = QuantumResistantState.Off, + selectedWireguardPort = Constraint.Any(), + availablePortRanges = emptyList() ) } } @@ -170,6 +201,8 @@ sealed class VpnSettingsDialogState { object ObfuscationInfoDialog : VpnSettingsDialogState() object QuantumResistanceInfoDialog : VpnSettingsDialogState() + + object WireguardPortInfoDialog : VpnSettingsDialogState() } sealed interface StagedDns { diff --git a/android/app/src/main/res/values-da/strings.xml b/android/app/src/main/res/values-da/strings.xml index 38d8f0159c..8379e714a5 100644 --- a/android/app/src/main/res/values-da/strings.xml +++ b/android/app/src/main/res/values-da/strings.xml @@ -184,4 +184,5 @@ <string name="we_will_look_into_this">Vi vil undersøge dette.</string> <string name="wireguard_mtu">WireGuard MTU</string> <string name="wireguard_mtu_footer">Indstil WireGuard MTU-værdi. Gyldigt område: %1$d - %2$d.</string> + <string name="wireguard_port_info_description">Den automatiske indstilling vælger tilfældigt fra de gyldige rækker af porte nedenfor.</string> </resources> diff --git a/android/app/src/main/res/values-de/strings.xml b/android/app/src/main/res/values-de/strings.xml index 4b0af82856..996bf50648 100644 --- a/android/app/src/main/res/values-de/strings.xml +++ b/android/app/src/main/res/values-de/strings.xml @@ -184,4 +184,5 @@ <string name="we_will_look_into_this">Wir werden uns das anschauen.</string> <string name="wireguard_mtu">WireGuard-MTU</string> <string name="wireguard_mtu_footer">WireGuard-MTU-Wert einstellen. Gültiger Bereich: %1$d – %2$d.</string> + <string name="wireguard_port_info_description">Die automatische Einstellung wählt zufällig aus den unten gezeigten gültigen Portbereichen.</string> </resources> diff --git a/android/app/src/main/res/values-es/strings.xml b/android/app/src/main/res/values-es/strings.xml index 2a2b536073..14d04fab32 100644 --- a/android/app/src/main/res/values-es/strings.xml +++ b/android/app/src/main/res/values-es/strings.xml @@ -184,4 +184,5 @@ <string name="we_will_look_into_this">Revisaremos esto.</string> <string name="wireguard_mtu">MTU de WireGuard</string> <string name="wireguard_mtu_footer">Establezca el valor de MTU de WireGuard. Intervalo válido: %1$d-%2$d.</string> + <string name="wireguard_port_info_description">El ajuste automático se elegirá al azar entre los rangos de puertos válidos que se muestran a continuación.</string> </resources> diff --git a/android/app/src/main/res/values-fi/strings.xml b/android/app/src/main/res/values-fi/strings.xml index 1682783eb2..6e03fc7cc5 100644 --- a/android/app/src/main/res/values-fi/strings.xml +++ b/android/app/src/main/res/values-fi/strings.xml @@ -184,4 +184,5 @@ <string name="we_will_look_into_this">Tutkimme asiaa.</string> <string name="wireguard_mtu">WireGuard MTU</string> <string name="wireguard_mtu_footer">Aseta WireGuardin MTU-arvo väliltä %1$d–%2$d.</string> + <string name="wireguard_port_info_description">Automaattinen asetus valitsee satunnaisesti käytettävissä olevista, alla luetelluista porteista.</string> </resources> diff --git a/android/app/src/main/res/values-fr/strings.xml b/android/app/src/main/res/values-fr/strings.xml index fd0312e947..a312b828f3 100644 --- a/android/app/src/main/res/values-fr/strings.xml +++ b/android/app/src/main/res/values-fr/strings.xml @@ -184,4 +184,5 @@ <string name="we_will_look_into_this">Nous allons nous pencher dessus.</string> <string name="wireguard_mtu">MTU WireGuard</string> <string name="wireguard_mtu_footer">Définir la valeur MTU WireGuard. Plage valide : %1$d - %2$d.</string> + <string name="wireguard_port_info_description">Le réglage automatique choisira au hasard parmi la plage de ports valide affichée ci-dessous.</string> </resources> diff --git a/android/app/src/main/res/values-it/strings.xml b/android/app/src/main/res/values-it/strings.xml index 7b15b5a504..f9d5eafc01 100644 --- a/android/app/src/main/res/values-it/strings.xml +++ b/android/app/src/main/res/values-it/strings.xml @@ -184,4 +184,5 @@ <string name="we_will_look_into_this">Verificheremo.</string> <string name="wireguard_mtu">MTU WireGuard</string> <string name="wireguard_mtu_footer">Imposta il valore MTU WireGuard. Intervallo valido: %1$d - %2$d.</string> + <string name="wireguard_port_info_description">L\'impostazione automatica sceglierà in modo casuale una porta valida negli intervalli mostrati di seguito.</string> </resources> diff --git a/android/app/src/main/res/values-ja/strings.xml b/android/app/src/main/res/values-ja/strings.xml index e66be1edf0..f9bc8ac000 100644 --- a/android/app/src/main/res/values-ja/strings.xml +++ b/android/app/src/main/res/values-ja/strings.xml @@ -184,4 +184,5 @@ <string name="we_will_look_into_this">この問題を調査いたします。</string> <string name="wireguard_mtu">WireGuard MTU</string> <string name="wireguard_mtu_footer">WireGuard MTUの値を設定します。有効範囲: %1$d ~ %2$d</string> + <string name="wireguard_port_info_description">自動設定では、以下の有効なポート範囲からランダムに選択されます。</string> </resources> diff --git a/android/app/src/main/res/values-ko/strings.xml b/android/app/src/main/res/values-ko/strings.xml index 2d4104a9b8..d0f3d31ca4 100644 --- a/android/app/src/main/res/values-ko/strings.xml +++ b/android/app/src/main/res/values-ko/strings.xml @@ -184,4 +184,5 @@ <string name="we_will_look_into_this">조사해보겠습니다.</string> <string name="wireguard_mtu">WireGuard MTU</string> <string name="wireguard_mtu_footer">WireGuard MTU 값을 설정하세요. 유효 범위: %1$d ~ %2$d</string> + <string name="wireguard_port_info_description">자동 설정은 아래 표시된 유효한 포트 범위에서 임의로 선택합니다.</string> </resources> diff --git a/android/app/src/main/res/values-my/strings.xml b/android/app/src/main/res/values-my/strings.xml index b9c0107722..d7e9372363 100644 --- a/android/app/src/main/res/values-my/strings.xml +++ b/android/app/src/main/res/values-my/strings.xml @@ -184,4 +184,5 @@ <string name="we_will_look_into_this">ဤသည်ကို စစ်ဆေးလိုက်ပါမည်။</string> <string name="wireguard_mtu">WireGuard MTU</string> <string name="wireguard_mtu_footer">WireGuard MTU တန်ဖိုးကို သတ်မှတ်ပါ။ အကျုံးဝင်သည့် အပိုင်းအခြား- %1$d - %2$d ။</string> + <string name="wireguard_port_info_description">အော်တိုဆက်တင်သည် အောက်တွင် ဖော်ပြထားသည့် အကျုံးဝင် ပေါ့တ် အပိုင်းအခြားများထဲမှ ကျပန်းရွေးချယ်ပါမည်။</string> </resources> diff --git a/android/app/src/main/res/values-nb/strings.xml b/android/app/src/main/res/values-nb/strings.xml index 4e2db90074..03492e41c6 100644 --- a/android/app/src/main/res/values-nb/strings.xml +++ b/android/app/src/main/res/values-nb/strings.xml @@ -184,4 +184,5 @@ <string name="we_will_look_into_this">Dette skal vi følge opp.</string> <string name="wireguard_mtu">WireGuard MTU</string> <string name="wireguard_mtu_footer">Angi WireGuard MTU-verdi. Verdiområde: %1$d–%2$d.</string> + <string name="wireguard_port_info_description">Den automatiske innstillingen vil tilfeldig velge fra utvalget av gyldige porter vist under.</string> </resources> diff --git a/android/app/src/main/res/values-nl/strings.xml b/android/app/src/main/res/values-nl/strings.xml index ffa697d76a..e6f5a6fff6 100644 --- a/android/app/src/main/res/values-nl/strings.xml +++ b/android/app/src/main/res/values-nl/strings.xml @@ -184,4 +184,5 @@ <string name="we_will_look_into_this">We gaan het bekijken.</string> <string name="wireguard_mtu">WireGuard-MTU</string> <string name="wireguard_mtu_footer">Stel de MTU-waarde voor WireGuard in. Geldig bereik: %1$d - %2$d.</string> + <string name="wireguard_port_info_description">Bij de automatische instelling wordt willekeurig gekozen uit de hieronder weergegeven geldige poortbereiken.</string> </resources> diff --git a/android/app/src/main/res/values-pl/strings.xml b/android/app/src/main/res/values-pl/strings.xml index c0f15a2c02..4ff503ec6d 100644 --- a/android/app/src/main/res/values-pl/strings.xml +++ b/android/app/src/main/res/values-pl/strings.xml @@ -184,4 +184,5 @@ <string name="we_will_look_into_this">Sprawdzimy to.</string> <string name="wireguard_mtu">MTU WireGuard</string> <string name="wireguard_mtu_footer">Ustaw wartość MTU WireGuard. Prawidłowy zakres: %1$d–%2$d.</string> + <string name="wireguard_port_info_description">Ustawienie automatyczne skutkuje wyborem losowym prawidłowego zakresu portów spośród zakresów przedstawionych poniżej.</string> </resources> diff --git a/android/app/src/main/res/values-pt/strings.xml b/android/app/src/main/res/values-pt/strings.xml index e578fdd98c..3037dd4eed 100644 --- a/android/app/src/main/res/values-pt/strings.xml +++ b/android/app/src/main/res/values-pt/strings.xml @@ -184,4 +184,5 @@ <string name="we_will_look_into_this">Vamos analisar esta situação.</string> <string name="wireguard_mtu">WireGuard MTU</string> <string name="wireguard_mtu_footer">Definir o valor WireGuard MTU. Intervalo válido: %1$d - %2$d.</string> + <string name="wireguard_port_info_description">A definição automática escolherá aleatoriamente a partir do intervalo de portas válido apresentado abaixo.</string> </resources> diff --git a/android/app/src/main/res/values-ru/strings.xml b/android/app/src/main/res/values-ru/strings.xml index e0f937f5e3..33ec82869c 100644 --- a/android/app/src/main/res/values-ru/strings.xml +++ b/android/app/src/main/res/values-ru/strings.xml @@ -184,4 +184,5 @@ <string name="we_will_look_into_this">Мы рассмотрим эту проблему.</string> <string name="wireguard_mtu">MTU для WireGuard</string> <string name="wireguard_mtu_footer">Установите значение MTU для WireGuard. Диапазон значений: %1$d–%2$d.</string> + <string name="wireguard_port_info_description">При автоматической настройке порт будет выбираться случайным образом из допустимого диапазона, показанного ниже.</string> </resources> diff --git a/android/app/src/main/res/values-sv/strings.xml b/android/app/src/main/res/values-sv/strings.xml index b2d1298f51..dcd91bb97b 100644 --- a/android/app/src/main/res/values-sv/strings.xml +++ b/android/app/src/main/res/values-sv/strings.xml @@ -184,4 +184,5 @@ <string name="we_will_look_into_this">Vi kommer att undersöka detta.</string> <string name="wireguard_mtu">WireGuard MTU</string> <string name="wireguard_mtu_footer">Ange WireGuard MTU-värde. Giltigt intervall: %1$d–%2$d.</string> + <string name="wireguard_port_info_description">Den automatiska inställningen väljer slumpmässigt från giltiga portintervall som visas nedan.</string> </resources> diff --git a/android/app/src/main/res/values-th/strings.xml b/android/app/src/main/res/values-th/strings.xml index ba7e1e2124..4ef7f78671 100644 --- a/android/app/src/main/res/values-th/strings.xml +++ b/android/app/src/main/res/values-th/strings.xml @@ -184,4 +184,5 @@ <string name="we_will_look_into_this">เราจะตรวจสอบปัญหานี้</string> <string name="wireguard_mtu">WireGuard MTU</string> <string name="wireguard_mtu_footer">ตั้งค่า WireGuard MTU ช่วงที่ใช้ได้: %1$d - %2$d</string> + <string name="wireguard_port_info_description">การตั้งค่าอัตโนมัติจะเป็นการสุ่มเลือกจากช่วงพอร์ตที่ใช้งานได้ต่างๆ ซึ่งแสดงอยู่ด้านล่าง</string> </resources> diff --git a/android/app/src/main/res/values-tr/strings.xml b/android/app/src/main/res/values-tr/strings.xml index 655e0c45bd..252b74da9b 100644 --- a/android/app/src/main/res/values-tr/strings.xml +++ b/android/app/src/main/res/values-tr/strings.xml @@ -184,4 +184,5 @@ <string name="we_will_look_into_this">Bunu araştıracağız.</string> <string name="wireguard_mtu">WireGuard MTU</string> <string name="wireguard_mtu_footer">WireGuard MTU değerini ayarlayın. Geçerli aralık: %1$d - %2$d.</string> + <string name="wireguard_port_info_description">Otomatik ayar, aşağıda gösterilen geçerli port aralıklarından rastgele seçim yapar.</string> </resources> diff --git a/android/app/src/main/res/values-zh-rCN/strings.xml b/android/app/src/main/res/values-zh-rCN/strings.xml index 369dfdd73a..77930489f9 100644 --- a/android/app/src/main/res/values-zh-rCN/strings.xml +++ b/android/app/src/main/res/values-zh-rCN/strings.xml @@ -184,4 +184,5 @@ <string name="we_will_look_into_this">我们将对此进行调查。</string> <string name="wireguard_mtu">WireGuard MTU</string> <string name="wireguard_mtu_footer">设置 WireGuard MTU 值。有效范围:%1$d - %2$d。</string> + <string name="wireguard_port_info_description">自动设置将从下方显示的有效端口范围中随机选择。</string> </resources> diff --git a/android/app/src/main/res/values-zh-rTW/strings.xml b/android/app/src/main/res/values-zh-rTW/strings.xml index 2b408557ea..3d297f6e9f 100644 --- a/android/app/src/main/res/values-zh-rTW/strings.xml +++ b/android/app/src/main/res/values-zh-rTW/strings.xml @@ -184,4 +184,5 @@ <string name="we_will_look_into_this">我們會對此進行調查。</string> <string name="wireguard_mtu">WireGuard MTU</string> <string name="wireguard_mtu_footer">設定 WireGuard MTU 值。有效範圍:%1$d - %2$d。</string> + <string name="wireguard_port_info_description">自動設定將會隨機從下方顯示的有效連接埠範圍中進行選擇。</string> </resources> diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml index d4d506173c..0c3ba602a1 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml @@ -202,4 +202,6 @@ <string name="on">On</string> <string name="quantum_creating_secure_connection">CREATING QUANTUM SECURE CONNECTION</string> <string name="quantum_secure_connection">QUANTUM SECURE CONNECTION</string> + <string name="wireguard_port_title">WireGuard Port</string> + <string name="wireguard_port_info_description">The automatic setting will randomly choose from the valid port ranges shown below.</string> </resources> 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 a0a2e8ae91..ab784263fa 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 @@ -5,19 +5,33 @@ import androidx.lifecycle.viewModelScope import app.cash.turbine.test import io.mockk.every import io.mockk.mockk +import io.mockk.slot import io.mockk.unmockkAll import io.mockk.verify import kotlin.test.assertEquals +import kotlin.test.assertIs import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.test.UnconfinedTestDispatcher import kotlinx.coroutines.test.runTest import net.mullvad.mullvadvpn.TestCoroutineRule +import net.mullvad.mullvadvpn.assertLists +import net.mullvad.mullvadvpn.compose.state.VpnSettingsUiState +import net.mullvad.mullvadvpn.model.Constraint +import net.mullvad.mullvadvpn.model.Port +import net.mullvad.mullvadvpn.model.PortRange import net.mullvad.mullvadvpn.model.QuantumResistantState +import net.mullvad.mullvadvpn.model.RelayConstraints +import net.mullvad.mullvadvpn.model.RelaySettings import net.mullvad.mullvadvpn.model.Settings import net.mullvad.mullvadvpn.model.TunnelOptions +import net.mullvad.mullvadvpn.model.WireguardConstraints import net.mullvad.mullvadvpn.model.WireguardTunnelOptions import net.mullvad.mullvadvpn.repository.SettingsRepository +import net.mullvad.mullvadvpn.ui.serviceconnection.RelayListListener +import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnectionContainer +import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnectionManager +import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnectionState import org.apache.commons.validator.routines.InetAddressValidator import org.junit.After import org.junit.Before @@ -30,20 +44,34 @@ class VpnSettingsViewModelTest { private val mockSettingsRepository: SettingsRepository = mockk() private val mockInetAddressValidator: InetAddressValidator = mockk() private val mockResources: Resources = mockk() + private val mockServiceConnectionManager: ServiceConnectionManager = mockk() + + private val mockServiceConnectionContainer: ServiceConnectionContainer = mockk() + private val mockRelayListListener: RelayListListener = mockk() + private val portRangeSlot = slot<(List<PortRange>) -> Unit>() private val mockSettingsUpdate = MutableStateFlow<Settings?>(null) + private val mockConnectionState = + MutableStateFlow<ServiceConnectionState>(ServiceConnectionState.Disconnected) private lateinit var viewModel: VpnSettingsViewModel @Before fun setUp() { every { mockSettingsRepository.settingsUpdates } returns mockSettingsUpdate + every { mockServiceConnectionManager.connectionState } returns mockConnectionState + + every { mockServiceConnectionContainer.relayListListener } returns mockRelayListListener + + every { mockRelayListListener.onPortRangesChange = capture(portRangeSlot) } answers {} + every { mockRelayListListener.onPortRangesChange = null } answers {} viewModel = VpnSettingsViewModel( repository = mockSettingsRepository, inetAddressValidator = mockInetAddressValidator, resources = mockResources, + serviceConnectionManager = mockServiceConnectionManager, dispatcher = UnconfinedTestDispatcher() ) } @@ -67,7 +95,10 @@ class VpnSettingsViewModelTest { @Test fun test_update_quantum_resistant_default_state() = runTest { + // Arrange val expectedResistantState = QuantumResistantState.Off + + // Act, Assert viewModel.uiState.test { assertEquals(expectedResistantState, awaitItem().quantumResistant) } @@ -84,11 +115,80 @@ class VpnSettingsViewModelTest { every { mockSettings.tunnelOptions } returns mockTunnelOptions every { mockTunnelOptions.wireguard } returns mockWireguardTunnelOptions every { mockWireguardTunnelOptions.quantumResistant } returns expectedResistantState + every { mockSettings.relaySettings } returns mockk<RelaySettings.Normal>(relaxed = true) viewModel.uiState.test { assertEquals(defaultResistantState, awaitItem().quantumResistant) mockSettingsUpdate.value = mockSettings + mockConnectionState.value = + ServiceConnectionState.ConnectedReady(mockServiceConnectionContainer) + portRangeSlot.captured.invoke(emptyList()) assertEquals(expectedResistantState, awaitItem().quantumResistant) } } + + @Test + fun test_update_wireguard_port_state() = runTest { + // Arrange + val expectedPort: Constraint<Port> = Constraint.Only(Port(99)) + val mockSettings: Settings = mockk(relaxed = true) + val mockRelaySettings: RelaySettings.Normal = mockk() + val mockRelayConstraints: RelayConstraints = mockk() + val mockWireguardConstraints: WireguardConstraints = mockk() + + every { mockSettings.relaySettings } returns mockRelaySettings + every { mockRelaySettings.relayConstraints } returns mockRelayConstraints + every { mockRelayConstraints.wireguardConstraints } returns mockWireguardConstraints + every { mockWireguardConstraints.port } returns expectedPort + + // Act, Assert + viewModel.uiState.test { + assertIs<Constraint.Any<Port>>(awaitItem().selectedWireguardPort) + mockSettingsUpdate.value = mockSettings + mockConnectionState.value = + ServiceConnectionState.ConnectedReady(mockServiceConnectionContainer) + portRangeSlot.captured.invoke(emptyList()) + assertEquals(expectedPort, awaitItem().selectedWireguardPort) + } + } + + @Test + fun test_select_wireguard_port() = runTest { + // Arrange + val wireguardPort: Constraint<Port> = Constraint.Only(Port(99)) + val wireguardConstraints = WireguardConstraints(port = wireguardPort) + every { mockRelayListListener.selectedWireguardConstraints = any() } returns Unit + + // Act + mockConnectionState.value = + ServiceConnectionState.ConnectedReady(mockServiceConnectionContainer) + viewModel.onWireguardPortSelected(wireguardPort) + + // Assert + verify(exactly = 1) { + mockRelayListListener.selectedWireguardConstraints = wireguardConstraints + } + } + + @Test + fun test_update_port_range_state() = runTest { + // Arrange + val expectedPortRange = listOf<PortRange>(mockk(), mockk()) + val mockSettings: Settings = mockk(relaxed = true) + + every { mockSettings.relaySettings } returns mockk<RelaySettings.Normal>(relaxed = true) + + // Act, Assert + viewModel.uiState.test { + assertIs<VpnSettingsUiState.DefaultUiState>(awaitItem()) + mockSettingsUpdate.value = mockSettings + viewModel.onWireguardPortInfoClicked() + mockConnectionState.value = + ServiceConnectionState.ConnectedReady(mockServiceConnectionContainer) + portRangeSlot.captured.invoke(expectedPortRange) + val state = awaitItem() + assertIs<VpnSettingsUiState.WireguardPortInfoDialogUiState>(state) + assertLists(expectedPortRange, state.availablePortRanges) + } + } } |
