summaryrefslogtreecommitdiffhomepage
path: root/android/app
diff options
context:
space:
mode:
Diffstat (limited to 'android/app')
-rw-r--r--android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/dialog/DnsDialogTest.kt14
-rw-r--r--android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/VpnSettingsScreenTest.kt45
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/DnsCell.kt46
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/DnsDialog.kt16
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/preview/VpnSettingsUiStatePreviewParameterProvider.kt2
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/VpnSettingsScreen.kt17
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/VpnSettingsUiState.kt3
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/repository/SettingsRepository.kt2
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/DnsDialogViewModel.kt51
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/VpnSettingsViewModel.kt16
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/VpnSettingsViewModelState.kt7
-rw-r--r--android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/VpnSettingsViewModelTest.kt1
12 files changed, 155 insertions, 65 deletions
diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/dialog/DnsDialogTest.kt b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/dialog/DnsDialogTest.kt
index 56bdc562fd..44cc53a256 100644
--- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/dialog/DnsDialogTest.kt
+++ b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/dialog/DnsDialogTest.kt
@@ -21,9 +21,9 @@ class DnsDialogTest {
DnsDialogViewState(
input = "",
validationError = null,
- isLocal = false,
isAllowLanEnabled = false,
index = null,
+ isIpv6Enabled = true,
)
private fun ComposeContext.initDialog(
@@ -48,7 +48,7 @@ class DnsDialogTest {
fun testDnsDialogLanWarningShownWhenLanTrafficDisabledAndLocalAddressUsed() =
composeExtension.use {
// Arrange
- initDialog(defaultState.copy(isAllowLanEnabled = false, isLocal = true))
+ initDialog(defaultState.copy(isAllowLanEnabled = false, input = localIpAddress))
// Assert
onNodeWithText(LOCAL_DNS_SERVER_WARNING).assertExists()
@@ -58,7 +58,7 @@ class DnsDialogTest {
fun testDnsDialogLanWarningNotShownWhenLanTrafficEnabledAndLocalAddressUsed() =
composeExtension.use {
// Arrange
- initDialog(defaultState.copy(isAllowLanEnabled = true, isLocal = true))
+ initDialog(defaultState.copy(isAllowLanEnabled = true, input = localIpAddress))
// Assert
onNodeWithText(LOCAL_DNS_SERVER_WARNING).assertDoesNotExist()
@@ -68,7 +68,7 @@ class DnsDialogTest {
fun testDnsDialogLanWarningNotShownWhenLanTrafficEnabledAndNonLocalAddressUsed() =
composeExtension.use {
// Arrange
- initDialog(defaultState.copy(isAllowLanEnabled = true, isLocal = false))
+ initDialog(defaultState.copy(isAllowLanEnabled = true, input = publicIpAddress))
// Assert
onNodeWithText(LOCAL_DNS_SERVER_WARNING).assertDoesNotExist()
@@ -78,7 +78,7 @@ class DnsDialogTest {
fun testDnsDialogLanWarningNotShownWhenLanTrafficDisabledAndNonLocalAddressUsed() =
composeExtension.use {
// Arrange
- initDialog(defaultState.copy(isAllowLanEnabled = false, isLocal = false))
+ initDialog(defaultState.copy(isAllowLanEnabled = false, input = publicIpAddress))
// Assert
onNodeWithText(LOCAL_DNS_SERVER_WARNING).assertDoesNotExist()
@@ -105,7 +105,7 @@ class DnsDialogTest {
// Arrange
initDialog(
defaultState.copy(
- input = "192.168.0.1",
+ input = localIpAddress,
validationError = ValidationError.DuplicateAddress,
)
)
@@ -120,5 +120,7 @@ class DnsDialogTest {
"\"Local Network Sharing\" under VPN settings."
private const val invalidIpAddress = "300.300.300.300"
+ private const val localIpAddress = "192.168.0.1"
+ private const val publicIpAddress = "1.1.1.1"
}
}
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 073c81b6f8..75d4f20f6e 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
@@ -74,6 +74,7 @@ class VpnSettingsScreenTest {
navigateToUdp2TcpSettings: () -> Unit = {},
onToggleAutoStartAndConnectOnBoot: (Boolean) -> Unit = {},
onSelectDeviceIpVersion: (Constraint<IpVersion>) -> Unit = {},
+ onToggleIpv6Toggle: (Boolean) -> Unit = {},
) {
setContentWithTheme {
VpnSettingsScreen(
@@ -106,6 +107,7 @@ class VpnSettingsScreenTest {
navigateToUdp2TcpSettings = navigateToUdp2TcpSettings,
onToggleAutoStartAndConnectOnBoot = onToggleAutoStartAndConnectOnBoot,
onSelectDeviceIpVersion = onSelectDeviceIpVersion,
+ onToggleIpv6Toggle = onToggleIpv6Toggle,
)
}
}
@@ -154,9 +156,9 @@ class VpnSettingsScreenTest {
isCustomDnsEnabled = true,
customDnsItems =
listOf(
- CustomDnsItem(address = DUMMY_DNS_ADDRESS, false),
- CustomDnsItem(address = DUMMY_DNS_ADDRESS_2, false),
- CustomDnsItem(address = DUMMY_DNS_ADDRESS_3, false),
+ CustomDnsItem(address = DUMMY_DNS_ADDRESS, false, false),
+ CustomDnsItem(address = DUMMY_DNS_ADDRESS_2, false, false),
+ CustomDnsItem(address = DUMMY_DNS_ADDRESS_3, false, false),
),
)
)
@@ -176,7 +178,8 @@ class VpnSettingsScreenTest {
state =
VpnSettingsUiState.createDefault(
isCustomDnsEnabled = false,
- customDnsItems = listOf(CustomDnsItem(address = DUMMY_DNS_ADDRESS, false)),
+ customDnsItems =
+ listOf(CustomDnsItem(address = DUMMY_DNS_ADDRESS, false, false)),
)
)
onNodeWithTag(LAZY_LIST_VPN_SETTINGS_TEST_TAG)
@@ -196,7 +199,13 @@ class VpnSettingsScreenTest {
isCustomDnsEnabled = true,
isLocalNetworkSharingEnabled = true,
customDnsItems =
- listOf(CustomDnsItem(address = DUMMY_DNS_ADDRESS, isLocal = true)),
+ listOf(
+ CustomDnsItem(
+ address = DUMMY_DNS_ADDRESS,
+ isLocal = true,
+ isIpv6 = false,
+ )
+ ),
)
)
@@ -213,7 +222,13 @@ class VpnSettingsScreenTest {
VpnSettingsUiState.createDefault(
isCustomDnsEnabled = true,
customDnsItems =
- listOf(CustomDnsItem(address = DUMMY_DNS_ADDRESS, isLocal = false)),
+ listOf(
+ CustomDnsItem(
+ address = DUMMY_DNS_ADDRESS,
+ isLocal = false,
+ isIpv6 = false,
+ )
+ ),
)
)
@@ -230,7 +245,13 @@ class VpnSettingsScreenTest {
VpnSettingsUiState.createDefault(
isCustomDnsEnabled = true,
customDnsItems =
- listOf(CustomDnsItem(address = DUMMY_DNS_ADDRESS, isLocal = false)),
+ listOf(
+ CustomDnsItem(
+ address = DUMMY_DNS_ADDRESS,
+ isLocal = false,
+ isIpv6 = false,
+ )
+ ),
)
)
@@ -247,7 +268,13 @@ class VpnSettingsScreenTest {
VpnSettingsUiState.createDefault(
isCustomDnsEnabled = true,
customDnsItems =
- listOf(CustomDnsItem(address = DUMMY_DNS_ADDRESS, isLocal = true)),
+ listOf(
+ CustomDnsItem(
+ address = DUMMY_DNS_ADDRESS,
+ isLocal = true,
+ isIpv6 = false,
+ )
+ ),
)
)
@@ -417,7 +444,7 @@ class VpnSettingsScreenTest {
state =
VpnSettingsUiState.createDefault(
isCustomDnsEnabled = true,
- customDnsItems = listOf(CustomDnsItem("1.1.1.1", false)),
+ customDnsItems = listOf(CustomDnsItem("1.1.1.1", false, false)),
),
navigateToDns = mockedClickHandler,
)
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/DnsCell.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/DnsCell.kt
index 8fe5f32b99..cc9ed30d9d 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/DnsCell.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/DnsCell.kt
@@ -1,6 +1,7 @@
package net.mullvad.mullvadvpn.compose.cell
import androidx.compose.foundation.layout.RowScope
+import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Error
import androidx.compose.material.icons.rounded.Error
@@ -9,18 +10,26 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.alpha
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
-import androidx.compose.ui.unit.dp
import net.mullvad.mullvadvpn.R
import net.mullvad.mullvadvpn.lib.theme.AppTheme
+import net.mullvad.mullvadvpn.lib.theme.Dimens
+import net.mullvad.mullvadvpn.lib.theme.color.AlphaInvisible
+import net.mullvad.mullvadvpn.lib.theme.color.AlphaVisible
@Preview
@Composable
private fun PreviewDnsCell() {
AppTheme {
- DnsCell(address = "0.0.0.0", isUnreachableLocalDnsWarningVisible = true, onClick = {})
+ DnsCell(
+ address = "0.0.0.0",
+ isUnreachableLocalDnsWarningVisible = true,
+ isUnreachableIpv6DnsWarningVisible = false,
+ onClick = {},
+ )
}
}
@@ -28,22 +37,37 @@ private fun PreviewDnsCell() {
fun DnsCell(
address: String,
isUnreachableLocalDnsWarningVisible: Boolean,
+ isUnreachableIpv6DnsWarningVisible: Boolean,
onClick: () -> Unit,
modifier: Modifier = Modifier,
) {
val titleModifier = Modifier
- val startPadding = 54.dp
+ val startPadding = Dimens.cellStartPadding
BaseCell(
headlineContent = { DnsTitle(address = address, modifier = titleModifier) },
- bodyView = {
- if (isUnreachableLocalDnsWarningVisible) {
- Icon(
- imageVector = Icons.Rounded.Error,
- contentDescription = stringResource(id = R.string.confirm_local_dns),
- tint = MaterialTheme.colorScheme.error,
- )
- }
+ iconView = {
+ Icon(
+ modifier =
+ Modifier.padding(end = Dimens.verticalDividerPadding)
+ .alpha(
+ when {
+ isUnreachableLocalDnsWarningVisible ||
+ isUnreachableIpv6DnsWarningVisible -> AlphaVisible
+ else -> AlphaInvisible
+ }
+ ),
+ imageVector = Icons.Rounded.Error,
+ contentDescription =
+ when {
+ isUnreachableLocalDnsWarningVisible ->
+ stringResource(id = R.string.confirm_local_dns)
+ isUnreachableIpv6DnsWarningVisible ->
+ stringResource(id = R.string.confirm_ipv6_dns)
+ else -> null
+ },
+ tint = MaterialTheme.colorScheme.error,
+ )
},
onCellClicked = { onClick.invoke() },
background = MaterialTheme.colorScheme.surfaceContainerLow,
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/DnsDialog.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/DnsDialog.kt
index 6f9c8126c0..4fee24a8f9 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/DnsDialog.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/DnsDialog.kt
@@ -39,7 +39,7 @@ private fun PreviewDnsDialogEdit() {
@Preview
@Composable
private fun PreviewDnsDialogEditAllowLanDisabled() {
- AppTheme { DnsDialog(DnsDialogViewState("192.168.1.1", null, true, false, 0), {}, {}, {}, {}) }
+ AppTheme { DnsDialog(DnsDialogViewState("192.168.1.1", null, false, false, 0), {}, {}, {}, {}) }
}
data class DnsDialogNavArgs(val index: Int? = null, val initialValue: String? = null)
@@ -94,15 +94,15 @@ fun DnsDialog(
placeholderText = stringResource(R.string.custom_dns_hint),
errorText =
when {
- state.validationError is ValidationError.DuplicateAddress -> {
+ state.validationError is ValidationError.DuplicateAddress ->
stringResource(R.string.duplicate_address_warning)
- }
- state.isLocal && !state.isAllowLanEnabled -> {
+ // Ordering is important, as we consider the lan error to have higher
+ // priority than the ipv6 error
+ state.isLocal && !state.isAllowLanEnabled ->
stringResource(id = R.string.confirm_local_dns)
- }
- else -> {
- null
- }
+ state.isIpv6 && !state.isIpv6Enabled ->
+ stringResource(id = R.string.confirm_ipv6_dns)
+ else -> null
},
modifier = Modifier.fillMaxWidth(),
)
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/preview/VpnSettingsUiStatePreviewParameterProvider.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/preview/VpnSettingsUiStatePreviewParameterProvider.kt
index 796965d856..55c8802c7f 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/preview/VpnSettingsUiStatePreviewParameterProvider.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/preview/VpnSettingsUiStatePreviewParameterProvider.kt
@@ -21,7 +21,7 @@ class VpnSettingsUiStatePreviewParameterProvider : PreviewParameterProvider<VpnS
mtu = Mtu(MTU),
isLocalNetworkSharingEnabled = true,
isCustomDnsEnabled = true,
- customDnsItems = listOf(CustomDnsItem("0.0.0.0", false)),
+ customDnsItems = listOf(CustomDnsItem("0.0.0.0", false, false)),
contentBlockersOptions =
DefaultDnsOptions(
blockAds = true,
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 a257341175..0504201d58 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
@@ -142,6 +142,7 @@ private fun PreviewVpnSettings(
navigateToWireguardPortDialog = {},
navigateToServerIpOverrides = {},
onSelectDeviceIpVersion = {},
+ onToggleIpv6Toggle = {},
)
}
}
@@ -271,6 +272,7 @@ fun VpnSettings(
dropUnlessResumed { navigator.navigate(Udp2TcpSettingsDestination) },
onToggleAutoStartAndConnectOnBoot = vm::onToggleAutoStartAndConnectOnBoot,
onSelectDeviceIpVersion = vm::onDeviceIpVersionSelected,
+ onToggleIpv6Toggle = vm::setIpv6Enabled,
)
}
@@ -308,9 +310,9 @@ fun VpnSettingsScreen(
navigateToUdp2TcpSettings: () -> Unit,
onToggleAutoStartAndConnectOnBoot: (Boolean) -> Unit,
onSelectDeviceIpVersion: (ipVersion: Constraint<IpVersion>) -> Unit,
+ onToggleIpv6Toggle: (Boolean) -> Unit,
) {
var expandContentBlockersState by rememberSaveable { mutableStateOf(false) }
- val biggerPadding = 54.dp
val topPadding = 6.dp
ScaffoldWithMediumTopBar(
@@ -467,6 +469,7 @@ fun VpnSettingsScreen(
address = item.address,
isUnreachableLocalDnsWarningVisible =
item.isLocal && !state.isLocalNetworkSharingEnabled,
+ isUnreachableIpv6DnsWarningVisible = item.isIpv6 && !state.isIpv6Enabled,
onClick = { navigateToDns(index, item.address) },
modifier = Modifier.animateItem(),
)
@@ -484,7 +487,7 @@ fun VpnSettingsScreen(
},
bodyView = {},
background = MaterialTheme.colorScheme.surfaceContainerLow,
- startPadding = biggerPadding,
+ startPadding = Dimens.cellStartPaddingLarge,
)
}
}
@@ -682,6 +685,16 @@ fun VpnSettingsScreen(
}
item {
+ HeaderSwitchComposeCell(
+ title = stringResource(R.string.enable_ipv6),
+ isToggled = state.isIpv6Enabled,
+ isEnabled = true,
+ onCellClicked = { newValue -> onToggleIpv6Toggle(newValue) },
+ )
+ Spacer(modifier = Modifier.height(Dimens.cellVerticalSpacing))
+ }
+
+ item {
MtuComposeCell(mtuValue = state.mtu, onEditMtu = { navigateToMtuDialog(state.mtu) })
}
item { MtuSubtitle(modifier = Modifier.testTag(LAZY_LIST_LAST_ITEM_TEST_TAG)) }
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 ec069001cc..3756d547f9 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
@@ -26,6 +26,7 @@ data class VpnSettingsUiState(
val systemVpnSettingsAvailable: Boolean,
val autoStartAndConnectOnBoot: Boolean,
val deviceIpVersion: Constraint<IpVersion>,
+ val isIpv6Enabled: Boolean,
) {
val isCustomWireguardPort =
selectedWireguardPort is Constraint.Only &&
@@ -51,6 +52,7 @@ data class VpnSettingsUiState(
systemVpnSettingsAvailable: Boolean = false,
autoStartAndConnectOnBoot: Boolean = false,
deviceIpVersion: Constraint<IpVersion> = Constraint.Any,
+ isIpv6Enabled: Boolean = true,
) =
VpnSettingsUiState(
mtu,
@@ -68,6 +70,7 @@ data class VpnSettingsUiState(
systemVpnSettingsAvailable,
autoStartAndConnectOnBoot,
deviceIpVersion,
+ isIpv6Enabled,
)
}
}
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/repository/SettingsRepository.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/repository/SettingsRepository.kt
index e6b03ee599..e5453fe57b 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/repository/SettingsRepository.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/repository/SettingsRepository.kt
@@ -74,4 +74,6 @@ class SettingsRepository(
suspend fun setDaitaEnabled(enabled: Boolean) = managementService.setDaitaEnabled(enabled)
suspend fun setDaitaDirectOnly(enabled: Boolean) = managementService.setDaitaDirectOnly(enabled)
+
+ suspend fun setIpv6Enabled(enabled: Boolean) = managementService.setIpv6Enabled(enabled)
}
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/DnsDialogViewModel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/DnsDialogViewModel.kt
index ab067e8145..c79682039b 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/DnsDialogViewModel.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/DnsDialogViewModel.kt
@@ -7,6 +7,7 @@ import arrow.core.Either
import arrow.core.raise.either
import arrow.core.raise.ensure
import com.ramcosta.composedestinations.generated.destinations.DnsDestination
+import java.net.Inet6Address
import java.net.InetAddress
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
@@ -34,13 +35,22 @@ sealed interface DnsDialogSideEffect {
data class DnsDialogViewState(
val input: String,
val validationError: ValidationError?,
- val isLocal: Boolean,
val isAllowLanEnabled: Boolean,
+ val isIpv6Enabled: Boolean,
val index: Int?,
) {
val isNewEntry = index == null
+ val isIpv6: Boolean = input.isIpv6()
+ val isLocal: Boolean = input.isLocalAddress()
fun isValid() = validationError == null
+
+ private fun String.isLocalAddress(): Boolean =
+ isValid() && InetAddress.getByName(this).isLocalAddress()
+
+ private fun String.isIpv6(): Boolean = isValid() && InetAddress.getByName(this) is Inet6Address
+
+ private fun InetAddress.isLocalAddress(): Boolean = isLinkLocalAddress || isSiteLocalAddress
}
sealed class ValidationError {
@@ -67,12 +77,25 @@ class DnsDialogViewModel(
input,
currentIndex,
settings ->
- createViewState(settings.addresses(), currentIndex, settings.allowLan, input)
+ DnsDialogViewState(
+ input = input,
+ validationError =
+ input.validateDnsEntry(currentIndex, settings.addresses()).leftOrNull(),
+ isAllowLanEnabled = settings.allowLan,
+ isIpv6Enabled = settings.tunnelOptions.genericOptions.enableIpv6,
+ index = currentIndex,
+ )
}
.stateIn(
viewModelScope,
SharingStarted.Lazily,
- createViewState(emptyList(), null, false, _ipAddressInput.value),
+ DnsDialogViewState(
+ input = _ipAddressInput.value,
+ validationError = null,
+ isAllowLanEnabled = false,
+ isIpv6Enabled = false,
+ index = null,
+ ),
)
private val _uiSideEffect = Channel<DnsDialogSideEffect>()
@@ -82,20 +105,6 @@ class DnsDialogViewModel(
viewModelScope.launch { settings.emit(repository.settingsUpdates.filterNotNull().first()) }
}
- private fun createViewState(
- customDnsList: List<InetAddress>,
- currentIndex: Int?,
- isAllowLanEnabled: Boolean,
- input: String,
- ): DnsDialogViewState =
- DnsDialogViewState(
- input,
- input.validateDnsEntry(currentIndex, customDnsList).leftOrNull(),
- input.isLocalAddress(),
- isAllowLanEnabled = isAllowLanEnabled,
- currentIndex,
- )
-
private fun String.validateDnsEntry(
index: Int?,
dnsList: List<InetAddress>,
@@ -147,14 +156,6 @@ class DnsDialogViewModel(
return inetAddressValidator.isValid(this)
}
- private fun String.isLocalAddress(): Boolean {
- return isValidIp() && InetAddress.getByName(this).isLocalAddress()
- }
-
- private fun InetAddress.isLocalAddress(): Boolean {
- return isLinkLocalAddress || isSiteLocalAddress
- }
-
private fun InetAddress.isDuplicateDnsEntry(
currentIndex: Int? = null,
dnsList: List<InetAddress>,
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 2273ada5e0..aa00ebdca3 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
@@ -3,6 +3,7 @@ package net.mullvad.mullvadvpn.viewmodel
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import co.touchlab.kermit.Logger
+import java.net.Inet6Address
import java.net.InetAddress
import java.net.UnknownHostException
import kotlinx.coroutines.CoroutineDispatcher
@@ -85,6 +86,7 @@ class VpnSettingsViewModel(
systemVpnSettingsAvailable = systemVpnSettingsUseCase(),
autoStartAndConnectOnBoot = autoStartAndConnectOnBoot,
deviceIpVersion = settings?.getDeviceIpVersion() ?: Constraint.Any,
+ ipv6Enabled = settings?.tunnelOptions?.genericOptions?.enableIpv6 == true,
)
}
.stateIn(
@@ -253,6 +255,14 @@ class VpnSettingsViewModel(
}
}
+ fun setIpv6Enabled(enable: Boolean) {
+ viewModelScope.launch(dispatcher) {
+ repository.setIpv6Enabled(enable).onLeft {
+ _uiSideEffect.send(VpnSettingsSideEffect.ShowToast.GenericError)
+ }
+ }
+ }
+
private fun updateDefaultDnsOptionsViaRepository(contentBlockersOption: DefaultDnsOptions) =
viewModelScope.launch(dispatcher) {
repository
@@ -275,7 +285,11 @@ class VpnSettingsViewModel(
private fun List<InetAddress>.asStringAddressList(): List<CustomDnsItem> {
return map {
- CustomDnsItem(address = it.hostAddress ?: EMPTY_STRING, isLocal = it.isLocalAddress())
+ CustomDnsItem(
+ address = it.hostAddress ?: EMPTY_STRING,
+ isLocal = it.isLocalAddress(),
+ isIpv6 = it is Inet6Address,
+ )
}
}
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 6e91294257..f4f1a8dbcd 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
@@ -26,6 +26,7 @@ data class VpnSettingsViewModelState(
val systemVpnSettingsAvailable: Boolean,
val autoStartAndConnectOnBoot: Boolean,
val deviceIpVersion: Constraint<IpVersion>,
+ val ipv6Enabled: Boolean,
) {
val isCustomWireguardPort =
selectedWireguardPort is Constraint.Only &&
@@ -48,6 +49,7 @@ data class VpnSettingsViewModelState(
systemVpnSettingsAvailable,
autoStartAndConnectOnBoot,
deviceIpVersion,
+ ipv6Enabled,
)
companion object {
@@ -68,16 +70,17 @@ data class VpnSettingsViewModelState(
systemVpnSettingsAvailable = false,
autoStartAndConnectOnBoot = false,
deviceIpVersion = Constraint.Any,
+ ipv6Enabled = false,
)
}
}
-data class CustomDnsItem(val address: String, val isLocal: Boolean) {
+data class CustomDnsItem(val address: String, val isLocal: Boolean, val isIpv6: Boolean) {
companion object {
private const val EMPTY_STRING = ""
fun default(): CustomDnsItem {
- return CustomDnsItem(address = EMPTY_STRING, isLocal = false)
+ return CustomDnsItem(address = EMPTY_STRING, isLocal = false, isIpv6 = false)
}
}
}
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 9d7ed39622..c91d6d9a20 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
@@ -174,6 +174,7 @@ class VpnSettingsViewModelTest {
daitaSettings = DaitaSettings(enabled = false, directOnly = false),
),
dnsOptions = mockk(relaxed = true),
+ genericOptions = mockk(relaxed = true),
)
// Act, Assert