summaryrefslogtreecommitdiffhomepage
path: root/android/app
diff options
context:
space:
mode:
authorJonatan Rhodin <jonatan.rhodin@mullvad.net>2023-07-03 15:29:20 +0200
committerJonatan Rhodin <jonatan.rhodin@mullvad.net>2023-07-13 13:19:29 +0200
commit9cdbb9c78f1682cc9b9c3c78bef314ba12dbac28 (patch)
treea7f8f26af194304e1a3967f9d64db051e01a3e8e /android/app
parent80a0705ab69dea7041fbc9176f2b7f2dd5c139e5 (diff)
downloadmullvadvpn-9cdbb9c78f1682cc9b9c3c78bef314ba12dbac28.tar.xz
mullvadvpn-9cdbb9c78f1682cc9b9c3c78bef314ba12dbac28.zip
Add the ability to set a custom wireguard port
Diffstat (limited to 'android/app')
-rw-r--r--android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/VpnSettingsScreenTest.kt125
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/CustomPortCell.kt108
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/Button.kt10
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/CustomPortDialog.kt136
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/WireguardPortInfoDialog.kt15
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/VpnSettingsScreen.kt96
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/VpnSettingsUiState.kt16
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/test/ComposeTestTagConstants.kt4
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/textfield/CustomPortTextField.kt29
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/theme/Color.kt2
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/theme/dimensions/Dimensions.kt5
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/VpnSettingsFragment.kt5
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/ConstraintExtensions.kt10
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/PortConstraintExtensions.kt23
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/PortRangeExtensions.kt19
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/VpnSettingsViewModel.kt5
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/VpnSettingsViewModelState.kt17
-rw-r--r--android/app/src/main/res/values-da/strings.xml3
-rw-r--r--android/app/src/main/res/values-de/strings.xml3
-rw-r--r--android/app/src/main/res/values-es/strings.xml3
-rw-r--r--android/app/src/main/res/values-fi/strings.xml3
-rw-r--r--android/app/src/main/res/values-fr/strings.xml3
-rw-r--r--android/app/src/main/res/values-it/strings.xml3
-rw-r--r--android/app/src/main/res/values-ja/strings.xml3
-rw-r--r--android/app/src/main/res/values-ko/strings.xml3
-rw-r--r--android/app/src/main/res/values-my/strings.xml3
-rw-r--r--android/app/src/main/res/values-nb/strings.xml3
-rw-r--r--android/app/src/main/res/values-nl/strings.xml3
-rw-r--r--android/app/src/main/res/values-pl/strings.xml3
-rw-r--r--android/app/src/main/res/values-pt/strings.xml3
-rw-r--r--android/app/src/main/res/values-ru/strings.xml3
-rw-r--r--android/app/src/main/res/values-sv/strings.xml3
-rw-r--r--android/app/src/main/res/values-th/strings.xml3
-rw-r--r--android/app/src/main/res/values-tr/strings.xml3
-rw-r--r--android/app/src/main/res/values-zh-rCN/strings.xml3
-rw-r--r--android/app/src/main/res/values-zh-rTW/strings.xml3
-rw-r--r--android/app/src/main/res/values/strings.xml8
37 files changed, 637 insertions, 53 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 1b97a47914..47f7d8280c 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,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_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
import net.mullvad.mullvadvpn.model.Constraint
import net.mullvad.mullvadvpn.model.Port
@@ -623,6 +625,8 @@ class VpnSettingsScreenTest {
toastMessagesSharedFlow = MutableSharedFlow<String>().asSharedFlow()
)
}
+
+ // Act
composeTestRule
.onNodeWithTag(LAZY_LIST_TEST_TAG)
.performScrollToNode(
@@ -653,19 +657,21 @@ class VpnSettingsScreenTest {
toastMessagesSharedFlow = MutableSharedFlow<String>().asSharedFlow()
)
}
+
+ // Act
composeTestRule
.onNodeWithTag(LAZY_LIST_TEST_TAG)
.performScrollToNode(
hasTestTag(String.format(LAZY_LIST_WIREGUARD_PORT_ITEM_X_TEST_TAG, 53))
)
-
- // Assert
composeTestRule
.onNodeWithTagAndText(
testTag = String.format(LAZY_LIST_WIREGUARD_PORT_ITEM_X_TEST_TAG, 51820),
text = "51820"
)
.performClick()
+
+ // Assert
verify(exactly = 1) {
mockSelectWireguardPortSelectionListener.invoke(Constraint.Only(Port(51820)))
}
@@ -692,6 +698,121 @@ class VpnSettingsScreenTest {
.assertExists()
}
+ @Test
+ fun testShowWireguardCustomPortDialog() {
+ // Arrange
+ composeTestRule.setContent {
+ VpnSettingsScreen(
+ uiState =
+ VpnSettingsUiState.CustomPortDialogUiState(
+ availablePortRanges = listOf(PortRange(53, 53), PortRange(120, 121))
+ ),
+ toastMessagesSharedFlow = MutableSharedFlow<String>().asSharedFlow()
+ )
+ }
+
+ // Assert
+ composeTestRule.onNodeWithText("Valid ranges: 53, 120-121").assertExists()
+ }
+
+ @Test
+ fun testShowWireguardCustomPort() {
+ // Arrange
+ composeTestRule.setContent {
+ VpnSettingsScreen(
+ uiState =
+ VpnSettingsUiState.DefaultUiState(
+ selectedWireguardPort = Constraint.Only(Port(4000))
+ ),
+ toastMessagesSharedFlow = MutableSharedFlow<String>().asSharedFlow()
+ )
+ }
+
+ // Act
+ composeTestRule
+ .onNodeWithTag(LAZY_LIST_TEST_TAG)
+ .performScrollToNode(hasTestTag(LAZY_LIST_WIREGUARD_CUSTOM_PORT_TEXT_TEST_TAG))
+
+ // Assert
+ composeTestRule.onNodeWithText("4000").assertExists()
+ }
+
+ @Test
+ fun testClickWireguardCustomPortMainCell() {
+ // Arrange
+ val mockOnShowCustomPortDialog: () -> Unit = mockk(relaxed = true)
+ composeTestRule.setContent {
+ VpnSettingsScreen(
+ uiState = VpnSettingsUiState.DefaultUiState(),
+ onShowCustomPortDialog = mockOnShowCustomPortDialog,
+ toastMessagesSharedFlow = MutableSharedFlow<String>().asSharedFlow()
+ )
+ }
+
+ // Act
+ composeTestRule
+ .onNodeWithTag(LAZY_LIST_TEST_TAG)
+ .performScrollToNode(hasTestTag(LAZY_LIST_WIREGUARD_CUSTOM_PORT_TEXT_TEST_TAG))
+ composeTestRule.onNodeWithTag(LAZY_LIST_WIREGUARD_CUSTOM_PORT_TEXT_TEST_TAG).performClick()
+
+ // Assert
+ verify { mockOnShowCustomPortDialog.invoke() }
+ }
+
+ @Test
+ fun testClickWireguardCustomPortNumberCell() {
+ // Arrange
+ val mockOnShowCustomPortDialog: () -> Unit = mockk(relaxed = true)
+ composeTestRule.setContent {
+ VpnSettingsScreen(
+ uiState =
+ VpnSettingsUiState.DefaultUiState(
+ selectedWireguardPort = Constraint.Only(Port(4000))
+ ),
+ onShowCustomPortDialog = mockOnShowCustomPortDialog,
+ toastMessagesSharedFlow = MutableSharedFlow<String>().asSharedFlow()
+ )
+ }
+
+ // Act
+ composeTestRule
+ .onNodeWithTag(LAZY_LIST_TEST_TAG)
+ .performScrollToNode(hasTestTag(LAZY_LIST_WIREGUARD_CUSTOM_PORT_TEXT_TEST_TAG))
+ composeTestRule
+ .onNodeWithTag(testTag = LAZY_LIST_WIREGUARD_CUSTOM_PORT_NUMBER_TEST_TAG)
+ .performClick()
+
+ // Assert
+ verify { mockOnShowCustomPortDialog.invoke() }
+ }
+
+ @Test
+ fun testSelectWireguardCustomPort() {
+ // Arrange
+ val onWireguardPortSelected: (Constraint<Port>) -> Unit = mockk(relaxed = true)
+ composeTestRule.setContent {
+ VpnSettingsScreen(
+ uiState =
+ VpnSettingsUiState.DefaultUiState(
+ selectedWireguardPort = Constraint.Only(Port(4000))
+ ),
+ onWireguardPortSelected = onWireguardPortSelected,
+ toastMessagesSharedFlow = MutableSharedFlow<String>().asSharedFlow()
+ )
+ }
+
+ // Act
+ composeTestRule
+ .onNodeWithTag(LAZY_LIST_TEST_TAG)
+ .performScrollToNode(hasTestTag(LAZY_LIST_WIREGUARD_CUSTOM_PORT_TEXT_TEST_TAG))
+ composeTestRule
+ .onNodeWithTag(testTag = LAZY_LIST_WIREGUARD_CUSTOM_PORT_TEXT_TEST_TAG)
+ .performClick()
+
+ // Assert
+ verify { onWireguardPortSelected.invoke(Constraint.Only(Port(4000))) }
+ }
+
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/CustomPortCell.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/CustomPortCell.kt
new file mode 100644
index 0000000000..d15e8140d8
--- /dev/null
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/CustomPortCell.kt
@@ -0,0 +1,108 @@
+package net.mullvad.mullvadvpn.compose.cell
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.defaultMinSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.layout.wrapContentWidth
+import androidx.compose.material3.Icon
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.alpha
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.tooling.preview.Preview
+import net.mullvad.mullvadvpn.R
+import net.mullvad.mullvadvpn.compose.component.SpacedColumn
+import net.mullvad.mullvadvpn.compose.theme.AlphaInvisible
+import net.mullvad.mullvadvpn.compose.theme.AlphaVisible
+import net.mullvad.mullvadvpn.compose.theme.AppTheme
+import net.mullvad.mullvadvpn.compose.theme.Dimens
+
+@Preview
+@Composable
+fun PreviewCustomPortCell() {
+ AppTheme {
+ SpacedColumn(Modifier.background(MaterialTheme.colorScheme.background)) {
+ CustomPortCell(title = "Title", isSelected = true, port = "444")
+ CustomPortCell(title = "Title", isSelected = false, port = "")
+ }
+ }
+}
+
+@Composable
+fun CustomPortCell(
+ title: String,
+ isSelected: Boolean,
+ port: String,
+ mainTestTag: String = "",
+ numberTestTag: String = "",
+ onMainCellClicked: () -> Unit = {},
+ onPortCellClicked: () -> Unit = {}
+) {
+ Row(
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.Start,
+ modifier = Modifier.height(Dimens.cellHeight).fillMaxWidth()
+ ) {
+ Row(
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.Start,
+ modifier =
+ Modifier.clickable { onMainCellClicked() }
+ .height(Dimens.cellHeight)
+ .weight(1f)
+ .background(
+ if (isSelected) {
+ MaterialTheme.colorScheme.surface
+ } else {
+ MaterialTheme.colorScheme.secondaryContainer
+ }
+ )
+ .padding(start = Dimens.cellStartPadding)
+ .testTag(mainTestTag)
+ ) {
+ Icon(
+ painter = painterResource(id = R.drawable.icon_tick),
+ contentDescription = null,
+ tint = MaterialTheme.colorScheme.onPrimary,
+ modifier =
+ Modifier.padding(end = Dimens.selectableCellTextMargin)
+ .alpha(if (isSelected) AlphaVisible else AlphaInvisible)
+ )
+ BaseCellTitle(
+ title = title,
+ style = MaterialTheme.typography.labelLarge,
+ textAlign = TextAlign.Start
+ )
+ }
+ Spacer(modifier = Modifier.width(Dimens.verticalSpacer))
+ Box(
+ modifier =
+ Modifier.clickable { onPortCellClicked() }
+ .height(Dimens.cellHeight)
+ .wrapContentWidth()
+ .defaultMinSize(minWidth = Dimens.customPortBoxMinWidth)
+ .background(MaterialTheme.colorScheme.primary)
+ .testTag(numberTestTag)
+ ) {
+ Text(
+ text = port.ifEmpty { stringResource(id = R.string.port) },
+ color = MaterialTheme.colorScheme.onPrimary,
+ modifier = Modifier.align(Alignment.Center)
+ )
+ }
+ }
+}
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/Button.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/Button.kt
index e3885078fd..ef5e283c29 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/Button.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/Button.kt
@@ -10,12 +10,11 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
-import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
-import net.mullvad.mullvadvpn.R
+import net.mullvad.mullvadvpn.compose.theme.Dimens
@Composable
fun ActionButton(
@@ -32,11 +31,8 @@ fun ActionButton(
contentPadding = PaddingValues(0.dp),
modifier =
modifier
- .height(dimensionResource(id = R.dimen.button_height))
- .defaultMinSize(
- minWidth = 0.dp,
- minHeight = dimensionResource(id = R.dimen.button_height)
- )
+ .height(Dimens.buttonHeight)
+ .defaultMinSize(minWidth = 0.dp, minHeight = Dimens.buttonHeight)
.fillMaxWidth(),
colors = colors,
shape = MaterialTheme.shapes.small
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/CustomPortDialog.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/CustomPortDialog.kt
new file mode 100644
index 0000000000..c53b91dfae
--- /dev/null
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/CustomPortDialog.kt
@@ -0,0 +1,136 @@
+package net.mullvad.mullvadvpn.compose.dialog
+
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.AlertDialog
+import androidx.compose.material3.ButtonDefaults
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.tooling.preview.Preview
+import net.mullvad.mullvadvpn.R
+import net.mullvad.mullvadvpn.compose.component.ActionButton
+import net.mullvad.mullvadvpn.compose.textfield.CustomPortTextField
+import net.mullvad.mullvadvpn.compose.theme.AlphaDescription
+import net.mullvad.mullvadvpn.compose.theme.AlphaDisabled
+import net.mullvad.mullvadvpn.compose.theme.AlphaInactive
+import net.mullvad.mullvadvpn.compose.theme.AppTheme
+import net.mullvad.mullvadvpn.compose.theme.Dimens
+import net.mullvad.mullvadvpn.model.PortRange
+import net.mullvad.mullvadvpn.util.asString
+import net.mullvad.mullvadvpn.util.isPortInValidRanges
+
+@Preview
+@Composable
+fun PreviewCustomPortDialog() {
+ AppTheme {
+ CustomPortDialog(
+ onSave = {},
+ onReset = {},
+ customPort = "",
+ allowedPortRanges = listOf(PortRange(10, 10), PortRange(40, 50)),
+ showReset = true,
+ onDismissRequest = {}
+ )
+ }
+}
+
+@Composable
+fun CustomPortDialog(
+ customPort: String,
+ allowedPortRanges: List<PortRange>,
+ showReset: Boolean,
+ onSave: (customPortString: String) -> Unit,
+ onReset: () -> Unit,
+ onDismissRequest: () -> Unit
+) {
+ val port = remember { mutableStateOf(customPort) }
+
+ AlertDialog(
+ title = {
+ Text(
+ text = stringResource(id = R.string.custom_port_dialog_title),
+ style = MaterialTheme.typography.headlineSmall
+ )
+ },
+ confirmButton = {
+ Column {
+ ActionButton(
+ text = stringResource(id = R.string.custom_port_dialog_submit),
+ onClick = { onSave(port.value) },
+ colors =
+ ButtonDefaults.buttonColors(
+ containerColor = MaterialTheme.colorScheme.primary,
+ contentColor = MaterialTheme.colorScheme.onPrimary,
+ disabledContentColor =
+ MaterialTheme.colorScheme.onPrimary.copy(alpha = AlphaInactive),
+ disabledContainerColor =
+ MaterialTheme.colorScheme.onPrimary.copy(alpha = AlphaDisabled)
+ ),
+ isEnabled =
+ port.value.isNotEmpty() &&
+ allowedPortRanges.isPortInValidRanges(port.value.toInt())
+ )
+ if (showReset) {
+ ActionButton(
+ text = stringResource(R.string.custom_port_dialog_remove),
+ onClick = { onReset() },
+ modifier = Modifier.padding(top = Dimens.mediumPadding),
+ colors =
+ ButtonDefaults.buttonColors(
+ containerColor = MaterialTheme.colorScheme.error,
+ contentColor = MaterialTheme.colorScheme.onError,
+ )
+ )
+ }
+ ActionButton(
+ colors =
+ ButtonDefaults.buttonColors(
+ containerColor = MaterialTheme.colorScheme.primary,
+ contentColor = MaterialTheme.colorScheme.onPrimary,
+ ),
+ text = stringResource(id = R.string.cancel),
+ modifier = Modifier.padding(top = Dimens.mediumPadding),
+ onClick = onDismissRequest
+ )
+ }
+ },
+ text = {
+ Column {
+ CustomPortTextField(
+ value = port.value,
+ onSubmit = { input ->
+ if (input.isNotEmpty()) {
+ onSave(input)
+ } else {
+ onReset()
+ }
+ },
+ onValueChanged = { input -> port.value = input },
+ isValidValue =
+ port.value.isNotEmpty() &&
+ allowedPortRanges.isPortInValidRanges(port.value.toInt())
+ )
+ Spacer(modifier = Modifier.height(Dimens.smallPadding))
+ Text(
+ text =
+ stringResource(
+ id = R.string.custom_port_dialog_valid_ranges,
+ allowedPortRanges.asString()
+ ),
+ color = MaterialTheme.colorScheme.onBackground.copy(alpha = AlphaDescription),
+ style = MaterialTheme.typography.bodySmall
+ )
+ }
+ },
+ containerColor = MaterialTheme.colorScheme.background,
+ titleContentColor = MaterialTheme.colorScheme.onBackground,
+ onDismissRequest = onDismissRequest
+ )
+}
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
index 680b395898..f8ab950ae4 100644
--- 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
@@ -4,25 +4,14 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
import net.mullvad.mullvadvpn.R
import net.mullvad.mullvadvpn.model.PortRange
+import net.mullvad.mullvadvpn.util.asString
@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}")
- }
- }
- },
+ stringResource(id = R.string.wireguard_port_info_port_range, portRanges.asString()),
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 dd7e4e22c6..a3d0e58f1e 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
@@ -29,7 +29,6 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.compose.ui.platform.testTag
-import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
@@ -46,6 +45,7 @@ import net.mullvad.mullvadvpn.R
import net.mullvad.mullvadvpn.compose.cell.BaseCell
import net.mullvad.mullvadvpn.compose.cell.ContentBlockersDisableModeCellSubtitle
import net.mullvad.mullvadvpn.compose.cell.CustomDnsCellSubtitle
+import net.mullvad.mullvadvpn.compose.cell.CustomPortCell
import net.mullvad.mullvadvpn.compose.cell.DnsCell
import net.mullvad.mullvadvpn.compose.cell.ExpandableComposeCell
import net.mullvad.mullvadvpn.compose.cell.HeaderSwitchComposeCell
@@ -60,6 +60,7 @@ import net.mullvad.mullvadvpn.compose.component.CollapsingTopBar
import net.mullvad.mullvadvpn.compose.component.drawVerticalScrollbar
import net.mullvad.mullvadvpn.compose.dialog.ContentBlockersInfoDialog
import net.mullvad.mullvadvpn.compose.dialog.CustomDnsInfoDialog
+import net.mullvad.mullvadvpn.compose.dialog.CustomPortDialog
import net.mullvad.mullvadvpn.compose.dialog.DnsDialog
import net.mullvad.mullvadvpn.compose.dialog.LocalNetworkSharingInfoDialog
import net.mullvad.mullvadvpn.compose.dialog.MalwareInfoDialog
@@ -73,6 +74,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_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
import net.mullvad.mullvadvpn.compose.theme.AppTheme
import net.mullvad.mullvadvpn.compose.theme.Dimens
@@ -82,6 +85,8 @@ 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.util.isCustom
+import net.mullvad.mullvadvpn.util.toDisplayCustomPort
import net.mullvad.mullvadvpn.viewmodel.CustomDnsItem
@Preview
@@ -127,7 +132,10 @@ private fun PreviewVpnSettings() {
onSelectQuantumResistanceSetting = {},
onQuantumResistanceInfoClicked = {},
onWireguardPortSelected = {},
- onWireguardPortInfoClicked = {}
+ onWireguardPortInfoClicked = {},
+ onShowCustomPortDialog = {},
+ onCancelCustomPortDialogClick = {},
+ onCloseCustomPortDialog = {}
)
}
}
@@ -168,10 +176,12 @@ fun VpnSettingsScreen(
onSelectQuantumResistanceSetting: (quantumResistant: QuantumResistantState) -> Unit = {},
onQuantumResistanceInfoClicked: () -> Unit = {},
onWireguardPortSelected: (port: Constraint<Port>) -> Unit = {},
- onWireguardPortInfoClicked: () -> Unit = {}
+ onWireguardPortInfoClicked: () -> Unit = {},
+ onShowCustomPortDialog: () -> Unit = {},
+ onCancelCustomPortDialogClick: () -> Unit = {},
+ onCloseCustomPortDialog: () -> Unit = {}
) {
- val cellVerticalSpacing = dimensionResource(id = R.dimen.cell_label_vertical_padding)
- val cellHorizontalSpacing = dimensionResource(id = R.dimen.cell_left_padding)
+ val savedCustomPort = rememberSaveable { mutableStateOf<Constraint<Port>>(Constraint.Any()) }
when (uiState) {
is VpnSettingsUiState.MtuDialogUiState -> {
@@ -214,6 +224,24 @@ fun VpnSettingsScreen(
is VpnSettingsUiState.WireguardPortInfoDialogUiState -> {
WireguardPortInfoDialog(uiState.availablePortRanges, onDismissInfoClick)
}
+ is VpnSettingsUiState.CustomPortDialogUiState -> {
+ CustomPortDialog(
+ customPort = savedCustomPort.value.toDisplayCustomPort(),
+ allowedPortRanges = uiState.availablePortRanges,
+ onSave = { customPortString ->
+ onWireguardPortSelected(Constraint.Only(Port(customPortString.toInt())))
+ },
+ onReset = {
+ if (uiState.selectedWireguardPort.isCustom()) {
+ onWireguardPortSelected(Constraint.Any())
+ }
+ savedCustomPort.value = Constraint.Any()
+ onCloseCustomPortDialog()
+ },
+ showReset = savedCustomPort.value is Constraint.Only,
+ onDismissRequest = { onCancelCustomPortDialogClick() }
+ )
+ }
else -> {
// NOOP
}
@@ -226,6 +254,15 @@ fun VpnSettingsScreen(
val state = rememberCollapsingToolbarScaffoldState()
val progress = state.toolbarState.progress
+ LaunchedEffect(uiState.selectedWireguardPort) {
+ if (
+ uiState.selectedWireguardPort.isCustom() &&
+ uiState.selectedWireguardPort != savedCustomPort.value
+ ) {
+ savedCustomPort.value = uiState.selectedWireguardPort
+ }
+ }
+
CollapsingToolbarScaffold(
backgroundColor = MaterialTheme.colorScheme.background,
modifier = Modifier.fillMaxSize(),
@@ -273,7 +310,7 @@ fun VpnSettingsScreen(
state = lazyListState
) {
item {
- Spacer(modifier = Modifier.height(cellVerticalSpacing))
+ Spacer(modifier = Modifier.height(Dimens.cellLabelVerticalPadding))
HeaderSwitchComposeCell(
title = stringResource(R.string.auto_connect),
isToggled = uiState.isAutoConnectEnabled,
@@ -285,7 +322,7 @@ fun VpnSettingsScreen(
SwitchComposeSubtitleCell(text = stringResource(id = R.string.auto_connect_footer))
}
item {
- Spacer(modifier = Modifier.height(cellVerticalSpacing))
+ Spacer(modifier = Modifier.height(Dimens.cellLabelVerticalPadding))
HeaderSwitchComposeCell(
title = stringResource(R.string.local_network_sharing),
isToggled = uiState.isAllowLanEnabled,
@@ -295,7 +332,7 @@ fun VpnSettingsScreen(
)
}
item {
- Spacer(modifier = Modifier.height(cellVerticalSpacing))
+ Spacer(modifier = Modifier.height(Dimens.cellLabelVerticalPadding))
MtuComposeCell(mtuValue = uiState.mtu, onEditMtu = { onMtuCellClick() })
}
item { MtuSubtitle() }
@@ -368,10 +405,10 @@ fun VpnSettingsScreen(
ContentBlockersDisableModeCellSubtitle(
Modifier.background(MaterialTheme.colorScheme.secondary)
.padding(
- start = cellHorizontalSpacing,
+ start = Dimens.cellStartPadding,
top = topPadding,
- end = cellHorizontalSpacing,
- bottom = cellVerticalSpacing
+ end = Dimens.cellEndPadding,
+ bottom = Dimens.cellLabelVerticalPadding
)
)
}
@@ -379,7 +416,7 @@ fun VpnSettingsScreen(
}
itemWithDivider {
- Spacer(modifier = Modifier.height(cellVerticalSpacing))
+ Spacer(modifier = Modifier.height(Dimens.cellLabelVerticalPadding))
InformationComposeCell(
title = stringResource(R.string.obfuscation_title),
onInfoClicked = { onObfuscationInfoClick() }
@@ -408,7 +445,7 @@ fun VpnSettingsScreen(
}
itemWithDivider {
- Spacer(modifier = Modifier.height(cellVerticalSpacing))
+ Spacer(modifier = Modifier.height(Dimens.cellLabelVerticalPadding))
InformationComposeCell(
title = stringResource(R.string.quantum_resistant_title),
onInfoClicked = { onQuantumResistanceInfoClicked() }
@@ -439,7 +476,7 @@ fun VpnSettingsScreen(
}
itemWithDivider {
- Spacer(modifier = Modifier.height(cellVerticalSpacing))
+ Spacer(modifier = Modifier.height(Dimens.cellLabelVerticalPadding))
InformationComposeCell(
title = stringResource(id = R.string.wireguard_port_title),
onInfoClicked = onWireguardPortInfoClicked
@@ -465,8 +502,31 @@ fun VpnSettingsScreen(
}
}
+ itemWithDivider {
+ CustomPortCell(
+ title = stringResource(id = R.string.wireguard_custon_port_title),
+ isSelected = uiState.selectedWireguardPort.isCustom(),
+ port =
+ if (uiState.selectedWireguardPort.isCustom()) {
+ uiState.selectedWireguardPort.toDisplayCustomPort()
+ } else {
+ savedCustomPort.value.toDisplayCustomPort()
+ },
+ onMainCellClicked = {
+ if (savedCustomPort.value is Constraint.Only) {
+ onWireguardPortSelected(savedCustomPort.value)
+ } else {
+ onShowCustomPortDialog()
+ }
+ },
+ onPortCellClicked = { onShowCustomPortDialog() },
+ mainTestTag = LAZY_LIST_WIREGUARD_CUSTOM_PORT_TEXT_TEST_TAG,
+ numberTestTag = LAZY_LIST_WIREGUARD_CUSTOM_PORT_NUMBER_TEST_TAG
+ )
+ }
+
item {
- Spacer(modifier = Modifier.height(cellVerticalSpacing))
+ Spacer(modifier = Modifier.height(Dimens.cellLabelVerticalPadding))
HeaderSwitchComposeCell(
title = stringResource(R.string.enable_custom_dns),
isToggled = uiState.isCustomDnsEnabled,
@@ -511,10 +571,10 @@ fun VpnSettingsScreen(
Modifier.background(MaterialTheme.colorScheme.secondary)
.testTag(LAZY_LIST_LAST_ITEM_TEST_TAG)
.padding(
- start = cellHorizontalSpacing,
+ start = Dimens.cellStartPadding,
top = topPadding,
- end = cellHorizontalSpacing,
- bottom = cellVerticalSpacing,
+ end = Dimens.cellEndPadding,
+ bottom = Dimens.cellLabelVerticalPadding,
)
)
}
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 0413e52ed1..d6c9625668 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
@@ -124,7 +124,7 @@ sealed interface VpnSettingsUiState {
override val contentBlockersOptions: DefaultDnsOptions = DefaultDnsOptions(),
override val selectedObfuscation: SelectedObfuscation = SelectedObfuscation.Off,
override val quantumResistant: QuantumResistantState = QuantumResistantState.Off,
- override val selectedWireguardPort: Constraint<Port> = Constraint.Any(),
+ override val selectedWireguardPort: Constraint<Port> = Constraint.Any()
) : VpnSettingsUiState
data class QuantumResistanceInfoDialogUiState(
@@ -153,4 +153,18 @@ sealed interface VpnSettingsUiState {
override val selectedWireguardPort: Constraint<Port> = Constraint.Any(),
val availablePortRanges: List<PortRange> = emptyList()
) : VpnSettingsUiState
+
+ data class CustomPortDialogUiState(
+ 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 84b9e32afe..536a343594 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
@@ -6,6 +6,10 @@ 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_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"
// SelectLocationScreen
const val CIRCULAR_PROGRESS_INDICATOR = "circular_progress_indicator"
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/textfield/CustomPortTextField.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/textfield/CustomPortTextField.kt
new file mode 100644
index 0000000000..956c65eedf
--- /dev/null
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/textfield/CustomPortTextField.kt
@@ -0,0 +1,29 @@
+package net.mullvad.mullvadvpn.compose.textfield
+
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.input.KeyboardType
+import net.mullvad.mullvadvpn.R
+
+@Composable
+fun CustomPortTextField(
+ value: String,
+ modifier: Modifier = Modifier,
+ onSubmit: (String) -> Unit,
+ onValueChanged: (String) -> Unit,
+ isValidValue: Boolean
+) {
+ CustomTextField(
+ value = value,
+ keyboardType = KeyboardType.Number,
+ modifier = modifier,
+ placeholderText = stringResource(id = R.string.custom_port_dialog_placeholder),
+ onValueChanged = onValueChanged,
+ onFocusChange = {},
+ onSubmit = onSubmit,
+ isDigitsOnlyAllowed = true,
+ isEnabled = true,
+ isValidValue = isValidValue
+ )
+}
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/theme/Color.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/theme/Color.kt
index 0efd386e50..d089a467ca 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/theme/Color.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/theme/Color.kt
@@ -22,5 +22,7 @@ val MullvadWhite60 = Color(0x99FFFFFF)
val MullvadWhite80 = Color(0xCCFFFFFF)
const val AlphaVisible = 1f
+const val AlphaDisabled = 0.2f
const val AlphaInactive = 0.4f
+const val AlphaDescription = 0.6f
const val AlphaInvisible = 0f
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/theme/dimensions/Dimensions.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/theme/dimensions/Dimensions.kt
index 3a0980e922..0d038fe10e 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/theme/dimensions/Dimensions.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/theme/dimensions/Dimensions.kt
@@ -4,6 +4,7 @@ import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
data class Dimensions(
+ val buttonHeight: Dp = 44.dp,
val cellEndPadding: Dp = 16.dp,
val cellFooterTopPadding: Dp = 6.dp,
val cellHeight: Dp = 52.dp,
@@ -11,6 +12,7 @@ data class Dimensions(
val cellStartPadding: Dp = 22.dp,
val cityRowPadding: Dp = 34.dp,
val countryRowPadding: Dp = 18.dp,
+ val customPortBoxMinWidth: Dp = 80.dp,
val expandableCellChevronSize: Dp = 30.dp,
val indentedCellStartPadding: Dp = 38.dp,
val infoButtonVerticalPadding: Dp = 13.dp,
@@ -33,7 +35,8 @@ data class Dimensions(
val sideMargin: Dp = 22.dp,
val smallPadding: Dp = 8.dp,
val titleIconSize: Dp = 24.dp,
- val verticalSpace: Dp = 20.dp
+ val verticalSpace: Dp = 20.dp,
+ val verticalSpacer: Dp = 1.dp
)
val defaultDimensions = Dimensions()
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 45c7f4b14e..c8a2a4e914 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
@@ -57,7 +57,10 @@ class VpnSettingsFragment : BaseFragment() {
onSelectQuantumResistanceSetting = vm::onSelectQuantumResistanceSetting,
onQuantumResistanceInfoClicked = vm::onQuantumResistanceInfoClicked,
onWireguardPortSelected = vm::onWireguardPortSelected,
- onWireguardPortInfoClicked = vm::onWireguardPortInfoClicked
+ onWireguardPortInfoClicked = vm::onWireguardPortInfoClicked,
+ onShowCustomPortDialog = vm::onShowCustomPortDialog,
+ onCancelCustomPortDialogClick = vm::onCancelDialogClick,
+ onCloseCustomPortDialog = vm::onCancelDialogClick
)
}
}
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
deleted file mode 100644
index 9be01b2952..0000000000
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/ConstraintExtensions.kt
+++ /dev/null
@@ -1,10 +0,0 @@
-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/util/PortConstraintExtensions.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/PortConstraintExtensions.kt
new file mode 100644
index 0000000000..0a5167da2e
--- /dev/null
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/PortConstraintExtensions.kt
@@ -0,0 +1,23 @@
+package net.mullvad.mullvadvpn.util
+
+import net.mullvad.mullvadvpn.constant.WIREGUARD_PRESET_PORTS
+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
+ }
+
+fun Constraint<Port>.isCustom() =
+ when (this) {
+ is Constraint.Any -> false
+ is Constraint.Only -> !WIREGUARD_PRESET_PORTS.contains(this.value.value)
+ }
+
+fun Constraint<Port>.toDisplayCustomPort() =
+ when (this) {
+ is Constraint.Any -> ""
+ is Constraint.Only -> this.value.value.toString()
+ }
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/PortRangeExtensions.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/PortRangeExtensions.kt
new file mode 100644
index 0000000000..7b7fa8b104
--- /dev/null
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/PortRangeExtensions.kt
@@ -0,0 +1,19 @@
+package net.mullvad.mullvadvpn.util
+
+import net.mullvad.mullvadvpn.model.PortRange
+
+fun List<PortRange>.isPortInValidRanges(port: Int) =
+ this.any { portRange -> portRange.from <= port && portRange.to >= port }
+
+fun List<PortRange>.asString() = buildString {
+ this@asString.forEachIndexed { index, range ->
+ if (index != 0) {
+ append(", ")
+ }
+ if (range.from == range.to) {
+ append(range.from)
+ } else {
+ append("${range.from}-${range.to}")
+ }
+ }
+}
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 a5fa4721bd..084e525d77 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
@@ -352,12 +352,17 @@ class VpnSettingsViewModel(
serviceConnectionManager.relayListListener()?.selectedWireguardConstraints =
WireguardConstraints(port = port)
}
+ hideDialog()
}
fun onWireguardPortInfoClicked() {
dialogState.update { VpnSettingsDialogState.WireguardPortInfoDialog }
}
+ fun onShowCustomPortDialog() {
+ dialogState.update { VpnSettingsDialogState.CustomPortDialog }
+ }
+
private fun updateDefaultDnsOptionsViaRepository(contentBlockersOption: DefaultDnsOptions) =
viewModelScope.launch(dispatcher) {
repository.setDnsOptions(
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 b77b107037..ab749e02d9 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
@@ -146,6 +146,21 @@ data class VpnSettingsViewModelState(
availablePortRanges = availablePortRanges
)
}
+ is VpnSettingsDialogState.CustomPortDialog -> {
+ VpnSettingsUiState.CustomPortDialogUiState(
+ mtu = mtuValue,
+ isAutoConnectEnabled = isAutoConnectEnabled,
+ isLocalNetworkSharingEnabled = isLocalNetworkSharingEnabled,
+ isCustomDnsEnabled = isCustomDnsEnabled,
+ isAllowLanEnabled = isAllowLanEnabled,
+ customDnsItems = customDnsList,
+ contentBlockersOptions = contentBlockersOptions,
+ selectedObfuscation = selectedObfuscation,
+ quantumResistant = quantumResistant,
+ selectedWireguardPort = selectedWireguardPort,
+ availablePortRanges = availablePortRanges
+ )
+ }
else ->
VpnSettingsUiState.DefaultUiState(
mtu = mtuValue,
@@ -203,6 +218,8 @@ sealed class VpnSettingsDialogState {
object QuantumResistanceInfoDialog : VpnSettingsDialogState()
object WireguardPortInfoDialog : VpnSettingsDialogState()
+
+ object CustomPortDialog : 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 f82406aa23..bcfaa59ac0 100644
--- a/android/app/src/main/res/values-da/strings.xml
+++ b/android/app/src/main/res/values-da/strings.xml
@@ -122,6 +122,7 @@
<string name="out_of_time">Tid udløbet</string>
<string name="paid_until">Betalt indtil</string>
<string name="pay_to_start_using">For at begynde at bruge appen skal du først føje tid til din konto.</string>
+ <string name="port">Port</string>
<string name="port_removal_notice">Dette vil slette alle videresendte porte. Lokale indstillinger vil blive gemt.</string>
<string name="privacy_disclaimer_title">Privatliv</string>
<string name="privacy_policy_label">Fortrolighedspolitik</string>
@@ -185,7 +186,9 @@
<string name="vpn_permission_error_notification_message">Altid-til VPN er måske aktiveret for en anden app</string>
<string name="vpn_permission_error_notification_title">VPN-tilladelsesfejl</string>
<string name="we_will_look_into_this">Vi vil undersøge dette.</string>
+ <string name="wireguard_custon_port_title">Brugerdefineret</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>
+ <string name="wireguard_port_info_port_range">Den brugerdefinerede port kan være en hvilken som helst værdi inden for de gyldige intervaller: %1$s.</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 fc423b7c63..911b145b3c 100644
--- a/android/app/src/main/res/values-de/strings.xml
+++ b/android/app/src/main/res/values-de/strings.xml
@@ -122,6 +122,7 @@
<string name="out_of_time">Zeit abgelaufen</string>
<string name="paid_until">Bezahlt bis</string>
<string name="pay_to_start_using">Um mit der Nutzung dieser App zu beginnen, müssen Sie erst einmal Zeit zu Ihrem Konto hinzufügen.</string>
+ <string name="port">Port</string>
<string name="port_removal_notice">Dadurch werden alle weitergeleiteten Ports gelöscht. Die lokalen Einstellungen werden gespeichert.</string>
<string name="privacy_disclaimer_title">Datenschutz</string>
<string name="privacy_policy_label">Datenschutzrichtlinie</string>
@@ -185,7 +186,9 @@
<string name="vpn_permission_error_notification_message">Always-on VPN könnte für eine andere App aktiviert sein</string>
<string name="vpn_permission_error_notification_title">VPN-Berechtigungsfehler</string>
<string name="we_will_look_into_this">Wir werden uns das anschauen.</string>
+ <string name="wireguard_custon_port_title">Benutzerdefiniert</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>
+ <string name="wireguard_port_info_port_range">Der benutzerdefinierte Port kann ein beliebiger Wert innerhalb dieser gültigen Bereiche sein: %1$s.</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 f365d518ae..49c9ee6fce 100644
--- a/android/app/src/main/res/values-es/strings.xml
+++ b/android/app/src/main/res/values-es/strings.xml
@@ -122,6 +122,7 @@
<string name="out_of_time">Tiempo agotado</string>
<string name="paid_until">Pagado hasta</string>
<string name="pay_to_start_using">Para empezar a usar la aplicación, primero necesita agregar tiempo a su cuenta.</string>
+ <string name="port">Puerto</string>
<string name="port_removal_notice">Se eliminarán todos los puertos reenviados. La configuración local se guardará.</string>
<string name="privacy_disclaimer_title">Privacidad</string>
<string name="privacy_policy_label">Política de privacidad</string>
@@ -185,7 +186,9 @@
<string name="vpn_permission_error_notification_message">La VPN siempre activa podría estar habilitada en otra aplicación</string>
<string name="vpn_permission_error_notification_title">Error en la autorización de la VPN</string>
<string name="we_will_look_into_this">Revisaremos esto.</string>
+ <string name="wireguard_custon_port_title">Personalizado</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>
+ <string name="wireguard_port_info_port_range">El puerto personalizado pueder ser cualquier valor dentro de los rangos válidos: %1$s.</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 f34f511072..a681bd4311 100644
--- a/android/app/src/main/res/values-fi/strings.xml
+++ b/android/app/src/main/res/values-fi/strings.xml
@@ -122,6 +122,7 @@
<string name="out_of_time">Ei käyttöaikaa</string>
<string name="paid_until">Maksu ennen</string>
<string name="pay_to_start_using">Voit aloittaa sovelluksen käyttämisen lisäämällä ensin aikaa tilillesi.</string>
+ <string name="port">Portti</string>
<string name="port_removal_notice">Toiminto poistaa kaikki välitetyt portit. Paikalliset asetukset tallennetaan.</string>
<string name="privacy_disclaimer_title">Tietosuoja</string>
<string name="privacy_policy_label">Tietosuojakäytäntö</string>
@@ -185,7 +186,9 @@
<string name="vpn_permission_error_notification_message">Aina päällä oleva VPN on mahdollisesti otettu käyttöön toiselle sovellukselle</string>
<string name="vpn_permission_error_notification_title">VPN-lupavirhe</string>
<string name="we_will_look_into_this">Tutkimme asiaa.</string>
+ <string name="wireguard_custon_port_title">Mukautettu</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>
+ <string name="wireguard_port_info_port_range">Mukautettu portti voi olla mikä tahansa sallittu arvo: %1$s.</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 e5434473c4..0a6a848969 100644
--- a/android/app/src/main/res/values-fr/strings.xml
+++ b/android/app/src/main/res/values-fr/strings.xml
@@ -122,6 +122,7 @@
<string name="out_of_time">Plus de temps</string>
<string name="paid_until">Payé jusqu\'au</string>
<string name="pay_to_start_using">Pour commencer à utiliser l\'application, vous devez d\'abord ajouter du temps à votre compte.</string>
+ <string name="port">Port</string>
<string name="port_removal_notice">Ceci supprimera tous les ports transférés. Les paramètres locaux seront enregistrés.</string>
<string name="privacy_disclaimer_title">Confidentialité</string>
<string name="privacy_policy_label">Politique de confidentialité</string>
@@ -185,7 +186,9 @@
<string name="vpn_permission_error_notification_message">« Toujours exiger un VPN » est peut-être activé pour une autre application</string>
<string name="vpn_permission_error_notification_title">Erreur de permission VPN</string>
<string name="we_will_look_into_this">Nous allons nous pencher dessus.</string>
+ <string name="wireguard_custon_port_title">Personnalisé</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>
+ <string name="wireguard_port_info_port_range">Le port personnalisé peut prendre n\'importe quelle valeur dans les plages valides : %1$s.</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 f005c90a47..4040af912e 100644
--- a/android/app/src/main/res/values-it/strings.xml
+++ b/android/app/src/main/res/values-it/strings.xml
@@ -122,6 +122,7 @@
<string name="out_of_time">Scaduto</string>
<string name="paid_until">Pagato fino al</string>
<string name="pay_to_start_using">Per iniziare a utilizzare l\'app, devi prima aggiungere tempo al tuo account.</string>
+ <string name="port">Porta</string>
<string name="port_removal_notice">Questa operazione eliminerà tutte le porte inoltrate. Le impostazioni locali verranno salvate.</string>
<string name="privacy_disclaimer_title">Privacy</string>
<string name="privacy_policy_label">Informativa sulla privacy</string>
@@ -185,7 +186,9 @@
<string name="vpn_permission_error_notification_message">La VPN sempre attiva potrebbe essere abilitata per un\'altra app</string>
<string name="vpn_permission_error_notification_title">Errore di autorizzazione VPN</string>
<string name="we_will_look_into_this">Verificheremo.</string>
+ <string name="wireguard_custon_port_title">Personalizzato</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>
+ <string name="wireguard_port_info_port_range">La porta personalizzata può essere qualsiasi valore all\'interno degli intervalli validi: %1$s.</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 e50604b756..0b261161fc 100644
--- a/android/app/src/main/res/values-ja/strings.xml
+++ b/android/app/src/main/res/values-ja/strings.xml
@@ -122,6 +122,7 @@
<string name="out_of_time">時間切れ</string>
<string name="paid_until">次の日時まで支払い済み</string>
<string name="pay_to_start_using">アプリを使い始めるには、まずはアカウントに時間を追加する必要があります。</string>
+ <string name="port">ポート</string>
<string name="port_removal_notice">これにより、転送されたポートがすべて削除されます。ローカル設定は保存されます。</string>
<string name="privacy_disclaimer_title">プライバシー</string>
<string name="privacy_policy_label">プライバシーポリシー</string>
@@ -185,7 +186,9 @@
<string name="vpn_permission_error_notification_message">Always-on VPNが別のアプリで有効になっている可能性があります</string>
<string name="vpn_permission_error_notification_title">VPN許可エラー</string>
<string name="we_will_look_into_this">この問題を調査いたします。</string>
+ <string name="wireguard_custon_port_title">カスタム</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>
+ <string name="wireguard_port_info_port_range">カスタムポートは次の有効範囲内の任意の値に設定できます: %1$s。</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 72206d1f2d..90f3d04b38 100644
--- a/android/app/src/main/res/values-ko/strings.xml
+++ b/android/app/src/main/res/values-ko/strings.xml
@@ -122,6 +122,7 @@
<string name="out_of_time">시간 초과</string>
<string name="paid_until">유효 기간</string>
<string name="pay_to_start_using">앱 사용을 시작하려면, 먼저 계정에 시간을 추가해야 합니다.</string>
+ <string name="port">포트</string>
<string name="port_removal_notice">전달된 모든 포트가 삭제됩니다. 로컬 설정이 저장됩니다.</string>
<string name="privacy_disclaimer_title">개인 정보 보호</string>
<string name="privacy_policy_label">개인정보 보호정책</string>
@@ -185,7 +186,9 @@
<string name="vpn_permission_error_notification_message">상시 접속 VPN이 다른 앱에 활성화되었을 수 있습니다.</string>
<string name="vpn_permission_error_notification_title">VPN 권한 오류</string>
<string name="we_will_look_into_this">조사해보겠습니다.</string>
+ <string name="wireguard_custon_port_title">사용자 지정</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>
+ <string name="wireguard_port_info_port_range">사용자 지정 포트는 유효한 범위 내의 모든 값이 될 수 있습니다: %1$s</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 ea41d88978..bc4728751f 100644
--- a/android/app/src/main/res/values-my/strings.xml
+++ b/android/app/src/main/res/values-my/strings.xml
@@ -122,6 +122,7 @@
<string name="out_of_time">အချိန်စေ့သွားပါပြီ</string>
<string name="paid_until">ဖော်ပြပါအထိ ပေးချေထားပြီး</string>
<string name="pay_to_start_using">အက်ပ်ကို စသုံးရန်အတွက် ဦးစွာ သင့်အကောင့်တွင် အချိန်ပေါင်းထည့်ပေးရန် လိုအပ်ပါသည်။</string>
+ <string name="port">ပေါ့တ်</string>
<string name="port_removal_notice">၎င်းသည် ပေးပို့ထားသော ports ကို ဖျက်ပါမည်။ စက်တွင်းဆက်တင်ကို သိမ်းထားပါမည်။</string>
<string name="privacy_disclaimer_title">ကိုယ်ရေးအချက်အလက် လုံခြုံရေး</string>
<string name="privacy_policy_label">ကိုယ်ပိုင်အချက်အလက် မူဝါဒ</string>
@@ -185,7 +186,9 @@
<string name="vpn_permission_error_notification_message">အမြဲဖွင့် VPN ကို နောက်ထပ်အက်ပ်အတွက် ဖွင့်ထားနိုင်ပါသည်</string>
<string name="vpn_permission_error_notification_title">VPN ခွင့်ပြုချက် ချို့ယွင်းချက်</string>
<string name="we_will_look_into_this">ဤသည်ကို စစ်ဆေးလိုက်ပါမည်။</string>
+ <string name="wireguard_custon_port_title">စိတ်ကြိုက်</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>
+ <string name="wireguard_port_info_port_range">စိတ်ကြိုက်ပေါ့တ်သည် အကျုံးဝင် အပိုင်းအခြားများထဲမှ မည်သည့်တန်ဖိုးမဆို ဖြစ်နိုင်ပါသည်- %1$s ။</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 847c9c8978..375d7c012c 100644
--- a/android/app/src/main/res/values-nb/strings.xml
+++ b/android/app/src/main/res/values-nb/strings.xml
@@ -122,6 +122,7 @@
<string name="out_of_time">Tiden har utløpt</string>
<string name="paid_until">Betalt fram til</string>
<string name="pay_to_start_using">For å starte bruken av appen, må du først legge til tid til kontoen.</string>
+ <string name="port">Port</string>
<string name="port_removal_notice">Dette vil slette alle formidlede porter. Lokale innstillinger blir lagret.</string>
<string name="privacy_disclaimer_title">Personvern</string>
<string name="privacy_policy_label">Retningslinjer for personvern</string>
@@ -185,7 +186,9 @@
<string name="vpn_permission_error_notification_message">VPN som alltid er på, kan være aktivert for en annen app</string>
<string name="vpn_permission_error_notification_title">Feil med VPN-tillatelse</string>
<string name="we_will_look_into_this">Dette skal vi følge opp.</string>
+ <string name="wireguard_custon_port_title">Egendefinert</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>
+ <string name="wireguard_port_info_port_range">Den egendefinerte porten kan ha en hvilken som helst verdi innen det gyldige utvalget: %1$s.</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 64a93d45d1..5d8124537f 100644
--- a/android/app/src/main/res/values-nl/strings.xml
+++ b/android/app/src/main/res/values-nl/strings.xml
@@ -122,6 +122,7 @@
<string name="out_of_time">Geen tijd meer</string>
<string name="paid_until">Betaald tot</string>
<string name="pay_to_start_using">Om de app te gebruiken, moet u eerst tijd toevoegen aan uw account.</string>
+ <string name="port">Poort</string>
<string name="port_removal_notice">Hiermee worden alle doorgestuurde poorten verwijderd. De lokale instellingen worden opgeslagen.</string>
<string name="privacy_disclaimer_title">Privacy</string>
<string name="privacy_policy_label">Privacybeleid</string>
@@ -185,7 +186,9 @@
<string name="vpn_permission_error_notification_message">Altijd-aan VPN is mogelijk ingeschakeld voor een andere app</string>
<string name="vpn_permission_error_notification_title">VPN-machtigingsfout</string>
<string name="we_will_look_into_this">We gaan het bekijken.</string>
+ <string name="wireguard_custon_port_title">Aangepast</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>
+ <string name="wireguard_port_info_port_range">De aangepaste poort kan elke waarde zijn binnen de geldige bereiken: %1$s.</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 90d87491ab..d38f5a7c39 100644
--- a/android/app/src/main/res/values-pl/strings.xml
+++ b/android/app/src/main/res/values-pl/strings.xml
@@ -122,6 +122,7 @@
<string name="out_of_time">Koniec czasu</string>
<string name="paid_until">Płatne do</string>
<string name="pay_to_start_using">Aby rozpocząć korzystanie z aplikacji, musisz najpierw dodać czas do swojego konta.</string>
+ <string name="port">Port</string>
<string name="port_removal_notice">Spowoduje to usunięcie wszystkich przekierowanych portów. Ustawienia lokalne zostaną zapisane.</string>
<string name="privacy_disclaimer_title">Prywatność</string>
<string name="privacy_policy_label">Polityka prywatności</string>
@@ -185,7 +186,9 @@
<string name="vpn_permission_error_notification_message">Opcja „Zawsze włączony VPN” może być włączona dla innej aplikacji</string>
<string name="vpn_permission_error_notification_title">Błąd uprawnienia VPN</string>
<string name="we_will_look_into_this">Sprawdzimy to.</string>
+ <string name="wireguard_custon_port_title">Niestandardowy</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>
+ <string name="wireguard_port_info_port_range">Port niestandardowy może mieć dowolną wartość z następujących prawidłowych zakresów: %1$s.</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 1cf2bbb289..29e3e5df25 100644
--- a/android/app/src/main/res/values-pt/strings.xml
+++ b/android/app/src/main/res/values-pt/strings.xml
@@ -122,6 +122,7 @@
<string name="out_of_time">Sem tempo</string>
<string name="paid_until">Pago até</string>
<string name="pay_to_start_using">Para começar a utilizar a aplicação, primeiro tem de adicionar tempo à sua conta.</string>
+ <string name="port">Porta</string>
<string name="port_removal_notice">Esta ação irá apagar todas as portas reencaminhadas. As definições locais serão guardadas.</string>
<string name="privacy_disclaimer_title">Privacidade</string>
<string name="privacy_policy_label">Política de privacidade</string>
@@ -185,7 +186,9 @@
<string name="vpn_permission_error_notification_message">A VPN sempre ligada pode estar ativada para outra app</string>
<string name="vpn_permission_error_notification_title">Erro de permissão da VPN</string>
<string name="we_will_look_into_this">Vamos analisar esta situação.</string>
+ <string name="wireguard_custon_port_title">Personalizado</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>
+ <string name="wireguard_port_info_port_range">A porta personalizada pode ser qualquer valor dentro dos intervalos válidos: %1$s.</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 d394aeb07d..3fdc285af6 100644
--- a/android/app/src/main/res/values-ru/strings.xml
+++ b/android/app/src/main/res/values-ru/strings.xml
@@ -122,6 +122,7 @@
<string name="out_of_time">Закончилось время</string>
<string name="paid_until">Оплачено до</string>
<string name="pay_to_start_using">Чтобы пользоваться приложением, нужно добавить время на учетную запись.</string>
+ <string name="port">Порт</string>
<string name="port_removal_notice">Будут удалены все перенаправленные порты. Локальные настройки сохранятся.</string>
<string name="privacy_disclaimer_title">Конфиденциальность</string>
<string name="privacy_policy_label">Политика конфиденциальности</string>
@@ -185,7 +186,9 @@
<string name="vpn_permission_error_notification_message">Опцию «VPN всегда вкл.» может быть включена для другого приложения</string>
<string name="vpn_permission_error_notification_title">Ошибка разрешения для VPN</string>
<string name="we_will_look_into_this">Мы рассмотрим эту проблему.</string>
+ <string name="wireguard_custon_port_title">Пользовательский</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>
+ <string name="wireguard_port_info_port_range">Пользовательский порт может принимать любое значение внутри допустимых диапазонов: %1$s.</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 1da6d39668..e85a4d2a20 100644
--- a/android/app/src/main/res/values-sv/strings.xml
+++ b/android/app/src/main/res/values-sv/strings.xml
@@ -122,6 +122,7 @@
<string name="out_of_time">Ingen tid kvar</string>
<string name="paid_until">Betalat till</string>
<string name="pay_to_start_using">Om du vill börja använda appen måste du först lägga till tid i ditt konto.</string>
+ <string name="port">Port</string>
<string name="port_removal_notice">Detta tar bort alla vidarebefordrade portar. Lokala inställningar sparas.</string>
<string name="privacy_disclaimer_title">Sekretess</string>
<string name="privacy_policy_label">Sekretesspolicy</string>
@@ -185,7 +186,9 @@
<string name="vpn_permission_error_notification_message">VPN som alltid är på kan ha aktiverats för annan app</string>
<string name="vpn_permission_error_notification_title">Behörighetsfel med VPN</string>
<string name="we_will_look_into_this">Vi kommer att undersöka detta.</string>
+ <string name="wireguard_custon_port_title">Anpassad</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>
+ <string name="wireguard_port_info_port_range">Den anpassade porten kan vara ett värde inom de giltiga intervallen: %1$s.</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 a85c2b8620..fefb659b84 100644
--- a/android/app/src/main/res/values-th/strings.xml
+++ b/android/app/src/main/res/values-th/strings.xml
@@ -122,6 +122,7 @@
<string name="out_of_time">หมดเวลา</string>
<string name="paid_until">ชำระเงินแล้วจนถึง</string>
<string name="pay_to_start_using">คุณจำเป็นต้องเพิ่มเวลาไปยังบัญชีของคุณก่อน เพื่อที่จะเริ่มใช้งานแอป</string>
+ <string name="port">พอร์ต</string>
<string name="port_removal_notice">นี่จะเป็นการลบพอร์ตที่ส่งต่อทั้งหมด และการตั้งค่าบนอุปกรณ์จะได้รับการบันทึกไว้</string>
<string name="privacy_disclaimer_title">ความเป็นส่วนตัว</string>
<string name="privacy_policy_label">นโยบายความเป็นส่วนตัว</string>
@@ -185,7 +186,9 @@
<string name="vpn_permission_error_notification_message">Always-on VPN อาจได้รับการเปิดใช้งานสำหรับแอปอื่น</string>
<string name="vpn_permission_error_notification_title">เกิดข้อผิดพลาดในการอนุญาต VPN</string>
<string name="we_will_look_into_this">เราจะตรวจสอบปัญหานี้</string>
+ <string name="wireguard_custon_port_title">กำหนดเอง</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>
+ <string name="wireguard_port_info_port_range">พอร์ตแบบกำหนดเองอาจมีค่าใดๆ ก็ได้ ภายในช่วงที่ใช้งานได้: %1$s</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 71672834ee..387db9c990 100644
--- a/android/app/src/main/res/values-tr/strings.xml
+++ b/android/app/src/main/res/values-tr/strings.xml
@@ -122,6 +122,7 @@
<string name="out_of_time">Süre doldu</string>
<string name="paid_until">Şu tarihe kadar ödendi:</string>
<string name="pay_to_start_using">Uygulamayı kullanmaya başlamak için önce hesabınıza süre eklemeniz gerekir.</string>
+ <string name="port">Port</string>
<string name="port_removal_notice">Bu işlem, yönlendirilen tüm portları silecek. Yerel ayarlar kaydedilecektir.</string>
<string name="privacy_disclaimer_title">Gizlilik</string>
<string name="privacy_policy_label">Gizlilik politikası</string>
@@ -185,7 +186,9 @@
<string name="vpn_permission_error_notification_message">Her zaman açık VPN başka bir uygulama için etkinleştirilebilir</string>
<string name="vpn_permission_error_notification_title">VPN izin hatası</string>
<string name="we_will_look_into_this">Bunu araştıracağız.</string>
+ <string name="wireguard_custon_port_title">Özel</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>
+ <string name="wireguard_port_info_port_range">Özel port, geçerli aralıklar içindeki herhangi bir değer olabilir: %1$s.</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 48099250e7..2362f35b97 100644
--- a/android/app/src/main/res/values-zh-rCN/strings.xml
+++ b/android/app/src/main/res/values-zh-rCN/strings.xml
@@ -122,6 +122,7 @@
<string name="out_of_time">已没有时间</string>
<string name="paid_until">到期时间</string>
<string name="pay_to_start_using">要开始使用本应用,您首先需要向帐户中充入时间。</string>
+ <string name="port">端口</string>
<string name="port_removal_notice">这将删除所有转发的端口。将保存本地设置。</string>
<string name="privacy_disclaimer_title">隐私</string>
<string name="privacy_policy_label">隐私政策</string>
@@ -185,7 +186,9 @@
<string name="vpn_permission_error_notification_message">可能为另一个应用启用了“始终启用 VPN”</string>
<string name="vpn_permission_error_notification_title">VPN 权限错误</string>
<string name="we_will_look_into_this">我们将对此进行调查。</string>
+ <string name="wireguard_custon_port_title">自定义</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>
+ <string name="wireguard_port_info_port_range">自定义端口可以是有效范围内的任何值:%1$s。</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 8d7fcd491d..7b3b65a5fd 100644
--- a/android/app/src/main/res/values-zh-rTW/strings.xml
+++ b/android/app/src/main/res/values-zh-rTW/strings.xml
@@ -122,6 +122,7 @@
<string name="out_of_time">逾時</string>
<string name="paid_until">支付至</string>
<string name="pay_to_start_using">需先在帳戶中加時,才能開始使用本應用程式。</string>
+ <string name="port">連接埠</string>
<string name="port_removal_notice">這將刪除所有轉送的連接埠。將儲存本機設定。</string>
<string name="privacy_disclaimer_title">隱私權</string>
<string name="privacy_policy_label">隱私權政策</string>
@@ -185,7 +186,9 @@
<string name="vpn_permission_error_notification_message">可能已為另一個應用程式啟用了「始終啟用 VPN」</string>
<string name="vpn_permission_error_notification_title">VPN 權限錯誤</string>
<string name="we_will_look_into_this">我們會對此進行調查。</string>
+ <string name="wireguard_custon_port_title">自訂</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>
+ <string name="wireguard_port_info_port_range">自訂連接埠可以是有效範圍內的任何值:%1$s。</string>
</resources>
diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml
index dfed235066..05e516c362 100644
--- a/android/app/src/main/res/values/strings.xml
+++ b/android/app/src/main/res/values/strings.xml
@@ -209,4 +209,12 @@
<![CDATA[No result for <b>%s</b>.]]>
</string>
<string name="select_location_empty_text_second_row">Try a different search.</string>
+ <string name="wireguard_port_info_port_range">The custom port can be any value inside the valid ranges: %s.</string>
+ <string name="wireguard_custon_port_title">Custom</string>
+ <string name="port">Port</string>
+ <string name="custom_port_dialog_title">WireGuard custom port</string>
+ <string name="custom_port_dialog_submit">Set port</string>
+ <string name="custom_port_dialog_remove">Remove custom port</string>
+ <string name="custom_port_dialog_valid_ranges">Valid ranges: %s</string>
+ <string name="custom_port_dialog_placeholder">Enter port</string>
</resources>