diff options
| author | Jonatan Rhodin <jonatan.rhodin@mullvad.net> | 2025-09-03 16:22:10 +0200 |
|---|---|---|
| committer | Jonatan Rhodin <jonatan.rhodin@mullvad.net> | 2025-09-03 16:22:10 +0200 |
| commit | 6755586be823effd7128420b0cf5cfe9b5bf3a61 (patch) | |
| tree | f7d1301e490f886499b230aef9d1aa5aa17c5cee /android/app/src/main | |
| parent | 82ef8c8fb924351172d2baf93b77023e6a408a90 (diff) | |
| parent | 675394de27138e98c8b171737114779834f7189d (diff) | |
| download | mullvadvpn-6755586be823effd7128420b0cf5cfe9b5bf3a61.tar.xz mullvadvpn-6755586be823effd7128420b0cf5cfe9b5bf3a61.zip | |
Merge branch 'bypass-quic-socket-android'
Diffstat (limited to 'android/app/src/main')
24 files changed, 271 insertions, 124 deletions
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/FilterRow.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/FilterRow.kt index 5ac7c765f0..710c4a5cac 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/FilterRow.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/FilterRow.kt @@ -65,6 +65,7 @@ fun FilterRow( is FilterChip.Daita -> DaitaFilterChip() is FilterChip.Entry -> EntryFilterChip() is FilterChip.Exit -> ExitFilterChip() + is FilterChip.Quic -> QuicFilterChip() } } } @@ -115,6 +116,15 @@ fun ExitFilterChip() { ) } +@Composable +fun QuicFilterChip() { + MullvadFilterChip( + text = stringResource(id = R.string.quic), + onRemoveClick = {}, + enabled = false, + ) +} + private fun Ownership.stringResources(): Int = when (this) { Ownership.MullvadOwned -> R.string.owned diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/ObfuscationModeCell.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/ObfuscationModeCell.kt index aa7416218a..bc0c52db83 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/ObfuscationModeCell.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/ObfuscationModeCell.kt @@ -122,6 +122,7 @@ private fun ObfuscationMode.toTitle() = ObfuscationMode.Off -> stringResource(id = R.string.off) ObfuscationMode.Udp2Tcp -> stringResource(id = R.string.upd_over_tcp) ObfuscationMode.Shadowsocks -> stringResource(id = R.string.shadowsocks) + ObfuscationMode.Quic -> stringResource(id = R.string.quic) } @Composable diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/connectioninfo/FeatureIndicatorsPanel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/connectioninfo/FeatureIndicatorsPanel.kt index a485dbe9d9..98abee9589 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/connectioninfo/FeatureIndicatorsPanel.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/connectioninfo/FeatureIndicatorsPanel.kt @@ -130,7 +130,8 @@ private fun FeatureIndicator.text(): String { FeatureIndicator.QUANTUM_RESISTANCE -> R.string.feature_quantum_resistant FeatureIndicator.SPLIT_TUNNELING -> R.string.split_tunneling FeatureIndicator.SHADOWSOCKS, - FeatureIndicator.UDP_2_TCP -> R.string.feature_udp_2_tcp + FeatureIndicator.UDP_2_TCP, + FeatureIndicator.QUIC -> R.string.feature_obfuscation FeatureIndicator.LAN_SHARING -> R.string.local_network_sharing FeatureIndicator.DNS_CONTENT_BLOCKERS -> R.string.dns_content_blockers FeatureIndicator.CUSTOM_DNS -> R.string.feature_custom_dns diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/ConnectScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/ConnectScreen.kt index 8f7775c613..8cb0bde801 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/ConnectScreen.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/ConnectScreen.kt @@ -791,6 +791,6 @@ private fun FeatureIndicator.destination() = FeatureIndicator.LAN_SHARING, FeatureIndicator.DNS_CONTENT_BLOCKERS, FeatureIndicator.CUSTOM_DNS, - FeatureIndicator.CUSTOM_MTU -> - VpnSettingsDestination(scrollToFeature = this, isModal = true) + FeatureIndicator.CUSTOM_MTU, + FeatureIndicator.QUIC -> VpnSettingsDestination(scrollToFeature = this, isModal = 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 ea45861c96..1b0db33b14 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 @@ -122,6 +122,7 @@ import net.mullvad.mullvadvpn.lib.ui.tag.LAZY_LIST_WIREGUARD_CUSTOM_PORT_TEXT_TE import net.mullvad.mullvadvpn.lib.ui.tag.LAZY_LIST_WIREGUARD_OBFUSCATION_TITLE_TEST_TAG import net.mullvad.mullvadvpn.lib.ui.tag.LAZY_LIST_WIREGUARD_PORT_ITEM_X_TEST_TAG import net.mullvad.mullvadvpn.lib.ui.tag.WIREGUARD_OBFUSCATION_OFF_CELL_TEST_TAG +import net.mullvad.mullvadvpn.lib.ui.tag.WIREGUARD_OBFUSCATION_QUIC_CELL_TEST_TAG import net.mullvad.mullvadvpn.lib.ui.tag.WIREGUARD_OBFUSCATION_SHADOWSOCKS_CELL_TEST_TAG import net.mullvad.mullvadvpn.lib.ui.tag.WIREGUARD_OBFUSCATION_UDP_OVER_TCP_CELL_TEST_TAG import net.mullvad.mullvadvpn.util.Lc @@ -475,7 +476,8 @@ fun VpnSettingsContent( val initialIndexFocus = when (initialScrollToFeature) { FeatureIndicator.UDP_2_TCP, - FeatureIndicator.SHADOWSOCKS -> VpnSettingItem.ObfuscationHeader::class + FeatureIndicator.SHADOWSOCKS, + FeatureIndicator.QUIC -> VpnSettingItem.ObfuscationHeader::class FeatureIndicator.LAN_SHARING -> VpnSettingItem.LocalNetworkSharingSetting::class FeatureIndicator.QUANTUM_RESISTANCE -> VpnSettingItem.QuantumResistanceHeader::class FeatureIndicator.DNS_CONTENT_BLOCKERS -> VpnSettingItem.DnsContentBlockersHeader::class @@ -852,6 +854,17 @@ fun VpnSettingsContent( ) } + is VpnSettingItem.ObfuscationItem.Quic -> + item(key = it::class.simpleName) { + SelectableCell( + title = stringResource(id = R.string.quic), + isSelected = it.selected, + modifier = Modifier.animateItem(), + testTag = WIREGUARD_OBFUSCATION_QUIC_CELL_TEST_TAG, + onCellClicked = { onSelectObfuscationMode(ObfuscationMode.Quic) }, + ) + } + is VpnSettingItem.QuantumItem -> item(key = it::class.simpleName + it.quantumResistantState) { SelectableCell( diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/location/SelectLocationList.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/location/SelectLocationList.kt index b7172865c2..ebe2e3ac2a 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/location/SelectLocationList.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/location/SelectLocationList.kt @@ -19,6 +19,7 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.intl.Locale import androidx.compose.ui.text.style.TextAlign @@ -44,6 +45,7 @@ import net.mullvad.mullvadvpn.lib.theme.AppTheme import net.mullvad.mullvadvpn.lib.theme.Dimens import net.mullvad.mullvadvpn.lib.theme.color.AlphaScrollbar import net.mullvad.mullvadvpn.lib.ui.component.relaylist.RelayListItem +import net.mullvad.mullvadvpn.lib.ui.tag.SELECT_LOCATION_LIST_TEST_TAG import net.mullvad.mullvadvpn.util.Lce import net.mullvad.mullvadvpn.viewmodel.location.SelectLocationListViewModel import org.koin.androidx.compose.koinViewModel @@ -136,7 +138,8 @@ private fun SelectLocationListContent( .drawVerticalScrollbar( lazyListState, MaterialTheme.colorScheme.onSurface.copy(alpha = AlphaScrollbar), - ), + ) + .testTag(SELECT_LOCATION_LIST_TEST_TAG), state = lazyListState, horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/VpnSettingItem.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/VpnSettingItem.kt index 517959e1a2..3ce6637115 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/VpnSettingItem.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/VpnSettingItem.kt @@ -104,6 +104,8 @@ sealed interface VpnSettingItem { data class UdpOverTcp(override val selected: Boolean, val port: Constraint<Port>) : ObfuscationItem + data class Quic(override val selected: Boolean) : ObfuscationItem + data class Off(override val selected: Boolean) : ObfuscationItem } 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 bca87015b4..08fafc56c0 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 @@ -205,6 +205,10 @@ data class VpnSettingsUiState(val settings: List<VpnSettingItem>, val isModal: B ) ) add(VpnSettingItem.Divider) + add( + VpnSettingItem.ObfuscationItem.Quic(obfuscationMode == ObfuscationMode.Quic) + ) + add(VpnSettingItem.Divider) add(VpnSettingItem.ObfuscationItem.Off(obfuscationMode == ObfuscationMode.Off)) add(VpnSettingItem.Spacer) diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayItemExtensions.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayItemExtensions.kt index f493aa97cb..c59b0124c3 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayItemExtensions.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayItemExtensions.kt @@ -2,8 +2,10 @@ package net.mullvad.mullvadvpn.relaylist import net.mullvad.mullvadvpn.lib.model.Constraint import net.mullvad.mullvadvpn.lib.model.GeoLocationId +import net.mullvad.mullvadvpn.lib.model.IpVersion import net.mullvad.mullvadvpn.lib.model.Ownership import net.mullvad.mullvadvpn.lib.model.Providers +import net.mullvad.mullvadvpn.lib.model.Quic import net.mullvad.mullvadvpn.lib.model.RelayItem fun RelayItem.children(): List<RelayItem> { @@ -57,13 +59,18 @@ fun RelayItem.CustomList.filter( ownership: Constraint<Ownership>, providers: Constraint<Providers>, daita: Boolean, + quic: Boolean, + ipVersion: Constraint<IpVersion>, ): RelayItem.CustomList { val newLocations = locations.mapNotNull { when (it) { - is RelayItem.Location.Country -> it.filter(ownership, providers, daita) - is RelayItem.Location.City -> it.filter(ownership, providers, daita) - is RelayItem.Location.Relay -> it.filter(ownership, providers, daita) + is RelayItem.Location.Country -> + it.filter(ownership, providers, daita, quic, ipVersion) + is RelayItem.Location.City -> + it.filter(ownership, providers, daita, quic, ipVersion) + is RelayItem.Location.Relay -> + it.filter(ownership, providers, daita, quic, ipVersion) } } return copy(locations = newLocations) @@ -73,8 +80,10 @@ fun RelayItem.Location.Country.filter( ownership: Constraint<Ownership>, providers: Constraint<Providers>, daita: Boolean, + quic: Boolean, + ipVersion: Constraint<IpVersion>, ): RelayItem.Location.Country? { - val cities = cities.mapNotNull { it.filter(ownership, providers, daita) } + val cities = cities.mapNotNull { it.filter(ownership, providers, daita, quic, ipVersion) } return if (cities.isNotEmpty()) { this.copy(cities = cities) } else { @@ -86,8 +95,10 @@ private fun RelayItem.Location.City.filter( ownership: Constraint<Ownership>, providers: Constraint<Providers>, daita: Boolean, + quic: Boolean, + ipVersion: Constraint<IpVersion>, ): RelayItem.Location.City? { - val relays = relays.mapNotNull { it.filter(ownership, providers, daita) } + val relays = relays.mapNotNull { it.filter(ownership, providers, daita, quic, ipVersion) } return if (relays.isNotEmpty()) { this.copy(relays = relays) } else { @@ -95,15 +106,38 @@ private fun RelayItem.Location.City.filter( } } -private fun RelayItem.Location.Relay.hasMatchingDaitaSetting(filterDaita: Boolean): Boolean = - if (filterDaita) daita else true +private fun RelayItem.Location.Relay.requiredFeatures( + requireDaita: Boolean, + requireQuic: Boolean, + ipVersion: Constraint<IpVersion>, +): Boolean = + when { + requireDaita && requireQuic -> daita && quic?.supports(ipVersion) == true + requireDaita -> daita + requireQuic -> quic?.supports(ipVersion) == true + else -> true + } + +private fun Quic.supports(ipVersion: Constraint<IpVersion>) = + when (ipVersion.getOrNull()) { + IpVersion.IPV4 -> supportsIpv4 + IpVersion.IPV6 -> supportsIpv6 + else -> inAddresses.isNotEmpty() + } private fun RelayItem.Location.Relay.filter( ownership: Constraint<Ownership>, providers: Constraint<Providers>, daita: Boolean, + quic: Boolean, + ipVersion: Constraint<IpVersion>, ): RelayItem.Location.Relay? = - if (hasMatchingDaitaSetting(daita) && hasOwnership(ownership) && hasProvider(providers)) this + if ( + requiredFeatures(daita, quic, ipVersion) && + hasOwnership(ownership) && + hasProvider(providers) + ) + this else null fun List<RelayItem.Location.Country>.findByGeoLocationId( diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/FilterChipUseCase.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/FilterChipUseCase.kt index 5e7a82d9ed..12ec3a296c 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/FilterChipUseCase.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/FilterChipUseCase.kt @@ -10,7 +10,10 @@ import net.mullvad.mullvadvpn.lib.model.Providers import net.mullvad.mullvadvpn.lib.model.Settings import net.mullvad.mullvadvpn.repository.RelayListFilterRepository import net.mullvad.mullvadvpn.repository.SettingsRepository +import net.mullvad.mullvadvpn.util.isDaitaAndDirectOnly +import net.mullvad.mullvadvpn.util.isQuicEnabled import net.mullvad.mullvadvpn.util.shouldFilterByDaita +import net.mullvad.mullvadvpn.util.shouldFilterByQuic typealias ModelOwnership = Ownership @@ -30,7 +33,7 @@ class FilterChipUseCase( selectedOwnership = selectedOwnership, selectedConstraintProviders = selectedConstraintProviders, providerToOwnerships = providerOwnership, - daitaDirectOnly = settings?.daitaAndDirectOnly() == true, + settings = settings, relayListType = relayListType, ) } @@ -39,7 +42,7 @@ class FilterChipUseCase( selectedOwnership: Constraint<Ownership>, selectedConstraintProviders: Constraint<Providers>, providerToOwnerships: Map<ProviderId, Set<Ownership>>, - daitaDirectOnly: Boolean, + settings: Settings?, relayListType: RelayListType, ): List<FilterChip> { val ownershipFilter = selectedOwnership.getOrNull() @@ -70,18 +73,19 @@ class FilterChipUseCase( } if ( shouldFilterByDaita( - daitaDirectOnly = daitaDirectOnly, + daitaDirectOnly = settings?.isDaitaAndDirectOnly() == true, relayListType = relayListType, ) ) { add(FilterChip.Daita) } + if ( + shouldFilterByQuic(settings?.isQuicEnabled() == true, relayListType = relayListType) + ) { + add(FilterChip.Quic) + } } } - - private fun Settings.daitaAndDirectOnly() = - tunnelOptions.wireguard.daitaSettings.enabled && - tunnelOptions.wireguard.daitaSettings.directOnly } sealed interface FilterChip { @@ -94,4 +98,6 @@ sealed interface FilterChip { data object Entry : FilterChip data object Exit : FilterChip + + data object Quic : FilterChip } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/FilteredRelayListUseCase.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/FilteredRelayListUseCase.kt index 875d63c1ce..2681cfa3ad 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/FilteredRelayListUseCase.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/FilteredRelayListUseCase.kt @@ -3,16 +3,20 @@ package net.mullvad.mullvadvpn.usecase import kotlinx.coroutines.flow.combine import net.mullvad.mullvadvpn.compose.state.RelayListType import net.mullvad.mullvadvpn.lib.model.Constraint +import net.mullvad.mullvadvpn.lib.model.IpVersion import net.mullvad.mullvadvpn.lib.model.Ownership import net.mullvad.mullvadvpn.lib.model.Providers import net.mullvad.mullvadvpn.lib.model.RelayItem -import net.mullvad.mullvadvpn.lib.model.Settings import net.mullvad.mullvadvpn.relaylist.filter import net.mullvad.mullvadvpn.repository.RelayListFilterRepository import net.mullvad.mullvadvpn.repository.RelayListRepository import net.mullvad.mullvadvpn.repository.SettingsRepository import net.mullvad.mullvadvpn.repository.WireguardConstraintsRepository +import net.mullvad.mullvadvpn.util.ipVersionConstraint +import net.mullvad.mullvadvpn.util.isDaitaAndDirectOnly +import net.mullvad.mullvadvpn.util.isQuicEnabled import net.mullvad.mullvadvpn.util.shouldFilterByDaita +import net.mullvad.mullvadvpn.util.shouldFilterByQuic class FilteredRelayListUseCase( private val relayListRepository: RelayListRepository, @@ -33,9 +37,15 @@ class FilteredRelayListUseCase( providers = selectedProviders, shouldFilterByDaita = shouldFilterByDaita( - daitaDirectOnly = settings?.daitaAndDirectOnly() == true, + daitaDirectOnly = settings?.isDaitaAndDirectOnly() == true, relayListType = relayListType, ), + shouldFilterByQuic = + shouldFilterByQuic( + settings?.isQuicEnabled() == true, + relayListType = relayListType, + ), + constraintIpVersion = settings?.ipVersionConstraint() ?: Constraint.Any, ) } @@ -43,9 +53,15 @@ class FilteredRelayListUseCase( ownership: Constraint<Ownership>, providers: Constraint<Providers>, shouldFilterByDaita: Boolean, - ) = mapNotNull { it.filter(ownership, providers, shouldFilterByDaita) } - - private fun Settings.daitaAndDirectOnly() = - tunnelOptions.wireguard.daitaSettings.enabled && - tunnelOptions.wireguard.daitaSettings.directOnly + shouldFilterByQuic: Boolean, + constraintIpVersion: Constraint<IpVersion>, + ) = mapNotNull { + it.filter( + ownership, + providers, + shouldFilterByDaita, + shouldFilterByQuic, + constraintIpVersion, + ) + } } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/RecentsUseCase.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/RecentsUseCase.kt index 7357068a56..ae2cea104d 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/RecentsUseCase.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/RecentsUseCase.kt @@ -2,6 +2,7 @@ package net.mullvad.mullvadvpn.usecase import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.filterIsInstance import kotlinx.coroutines.flow.map import net.mullvad.mullvadvpn.compose.state.MultihopRelayListType import net.mullvad.mullvadvpn.compose.state.RelayListType @@ -22,31 +23,42 @@ class RecentsUseCase( private val settingsRepository: SettingsRepository, ) { - operator fun invoke(): Flow<List<Hop>?> = + operator fun invoke(isMultihop: Boolean): Flow<List<Hop>?> = + if (isMultihop) { + multiHopRecents() + } else { + singleHopRecents() + } + + private fun singleHopRecents(): Flow<List<Hop.Single<RelayItem>>?> = combine( - recents(), + recents().map { it?.filterIsInstance<Recent.Singlehop>() }, + filteredRelayListUseCase(RelayListType.Single), + customListsRelayItemUseCase(RelayListType.Single), + ) { recents, relayList, customList -> + recents?.mapNotNull { recent -> + val relayListItem = recent.location.findItem(customList, relayList) + + relayListItem?.let { Hop.Single(it) } + } + } + + private fun multiHopRecents(): Flow<List<Hop.Multi>?> = + combine( + recents().map { it?.filterIsInstance<Recent.Multihop>() }, filteredRelayListUseCase(RelayListType.Multihop(MultihopRelayListType.ENTRY)), customListsRelayItemUseCase(RelayListType.Multihop(MultihopRelayListType.ENTRY)), filteredRelayListUseCase(RelayListType.Multihop(MultihopRelayListType.EXIT)), customListsRelayItemUseCase(RelayListType.Multihop(MultihopRelayListType.EXIT)), ) { recents, entryRelayList, entryCustomLists, exitRelayList, exitCustomLists -> recents?.mapNotNull { recent -> - when (recent) { - is Recent.Multihop -> { - val entry = recent.entry.findItem(entryCustomLists, entryRelayList) - val exit = recent.exit.findItem(exitCustomLists, exitRelayList) - - if (entry != null && exit != null) { - Hop.Multi(entry, exit) - } else { - null - } - } - is Recent.Singlehop -> { - val relayListItem = recent.location.findItem(exitCustomLists, exitRelayList) + val entry = recent.entry.findItem(entryCustomLists, entryRelayList) + val exit = recent.exit.findItem(exitCustomLists, exitRelayList) - relayListItem?.let { Hop.Single(it) } - } + if (entry != null && exit != null) { + Hop.Multi(entry, exit) + } else { + null } } } @@ -66,7 +78,7 @@ class RecentsUseCase( relayList: List<RelayItem.Location.Country>, ): RelayItem? = when (this) { - is CustomListId -> customLists.firstOrNull { this == it.id } + is CustomListId -> customLists.firstOrNull { this == it.id && it.hasChildren } is GeoLocationId -> relayList.findByGeoLocationId(this) } } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/customlists/FilterCustomListsRelayItemUseCase.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/customlists/FilterCustomListsRelayItemUseCase.kt index 243c0c643a..6604a76805 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/customlists/FilterCustomListsRelayItemUseCase.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/customlists/FilterCustomListsRelayItemUseCase.kt @@ -4,15 +4,19 @@ import kotlin.collections.mapNotNull import kotlinx.coroutines.flow.combine import net.mullvad.mullvadvpn.compose.state.RelayListType import net.mullvad.mullvadvpn.lib.model.Constraint +import net.mullvad.mullvadvpn.lib.model.IpVersion import net.mullvad.mullvadvpn.lib.model.Ownership import net.mullvad.mullvadvpn.lib.model.Providers import net.mullvad.mullvadvpn.lib.model.RelayItem -import net.mullvad.mullvadvpn.lib.model.Settings import net.mullvad.mullvadvpn.relaylist.filter import net.mullvad.mullvadvpn.repository.RelayListFilterRepository import net.mullvad.mullvadvpn.repository.SettingsRepository import net.mullvad.mullvadvpn.repository.WireguardConstraintsRepository +import net.mullvad.mullvadvpn.util.ipVersionConstraint +import net.mullvad.mullvadvpn.util.isDaitaAndDirectOnly +import net.mullvad.mullvadvpn.util.isQuicEnabled import net.mullvad.mullvadvpn.util.shouldFilterByDaita +import net.mullvad.mullvadvpn.util.shouldFilterByQuic class FilterCustomListsRelayItemUseCase( private val customListsRelayItemUseCase: CustomListsRelayItemUseCase, @@ -34,9 +38,15 @@ class FilterCustomListsRelayItemUseCase( providers = selectedProviders, daita = shouldFilterByDaita( - daitaDirectOnly = settings?.daitaAndDirectOnly() == true, + daitaDirectOnly = settings?.isDaitaAndDirectOnly() == true, relayListType = relayListType, ), + quic = + shouldFilterByQuic( + settings?.isQuicEnabled() == true, + relayListType = relayListType, + ), + ipVersion = settings?.ipVersionConstraint() ?: Constraint.Any, ) } @@ -44,9 +54,9 @@ class FilterCustomListsRelayItemUseCase( ownership: Constraint<Ownership>, providers: Constraint<Providers>, daita: Boolean, - ) = mapNotNull { it.filter(ownership, providers, daita = daita) } - - private fun Settings.daitaAndDirectOnly() = - tunnelOptions.wireguard.daitaSettings.enabled && - tunnelOptions.wireguard.daitaSettings.directOnly + quic: Boolean, + ipVersion: Constraint<IpVersion>, + ) = mapNotNull { + it.filter(ownership, providers, daita = daita, quic = quic, ipVersion = ipVersion) + } } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/Daita.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/Filter.kt index e4a0a9957a..4fa7ba231c 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/Daita.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/Filter.kt @@ -9,3 +9,10 @@ fun shouldFilterByDaita(daitaDirectOnly: Boolean, relayListType: RelayListType) is RelayListType.Multihop -> daitaDirectOnly && relayListType.multihopRelayListType == MultihopRelayListType.ENTRY } + +fun shouldFilterByQuic(isQuicEnabled: Boolean, relayListType: RelayListType) = + when (relayListType) { + RelayListType.Single -> isQuicEnabled + is RelayListType.Multihop -> + isQuicEnabled && relayListType.multihopRelayListType == MultihopRelayListType.ENTRY + } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/Settings.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/Settings.kt new file mode 100644 index 0000000000..4da463ab51 --- /dev/null +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/Settings.kt @@ -0,0 +1,36 @@ +package net.mullvad.mullvadvpn.util + +import net.mullvad.mullvadvpn.lib.model.DnsState +import net.mullvad.mullvadvpn.lib.model.ObfuscationMode +import net.mullvad.mullvadvpn.lib.model.Settings + +fun Settings.quantumResistant() = tunnelOptions.wireguard.quantumResistant + +fun Settings.isCustomDnsEnabled() = tunnelOptions.dnsOptions.state == DnsState.Custom + +fun Settings.customDnsAddresses() = tunnelOptions.dnsOptions.customOptions.addresses + +fun Settings.contentBlockersSettings() = tunnelOptions.dnsOptions.defaultOptions + +fun Settings.selectedObfuscationMode() = obfuscationSettings.selectedObfuscationMode + +fun Settings.wireguardPort() = relaySettings.relayConstraints.wireguardConstraints.port + +fun Settings.deviceIpVersion() = relaySettings.relayConstraints.wireguardConstraints.ipVersion + +fun Settings.isDaitaAndDirectOnly() = isDaitaEnabled() && isDaitaDirectOnly() + +fun Settings.isQuicEnabled() = obfuscationSettings.selectedObfuscationMode == ObfuscationMode.Quic + +fun Settings.ipVersionConstraint() = relaySettings.relayConstraints.wireguardConstraints.ipVersion + +fun Settings.isDaitaEnabled() = daitaSettings().enabled + +fun Settings.isDaitaDirectOnly() = daitaSettings().directOnly + +fun Settings.shadowSocksPort() = obfuscationSettings.shadowsocks.port + +fun Settings.isMultihopEnabled() = + relaySettings.relayConstraints.wireguardConstraints.isMultihopEnabled + +private fun Settings.daitaSettings() = tunnelOptions.wireguard.daitaSettings diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/DaitaViewModel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/DaitaViewModel.kt index f941b26455..fdaa9c7eb6 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/DaitaViewModel.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/DaitaViewModel.kt @@ -10,9 +10,10 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch import net.mullvad.mullvadvpn.compose.state.DaitaUiState -import net.mullvad.mullvadvpn.lib.model.Settings import net.mullvad.mullvadvpn.repository.SettingsRepository import net.mullvad.mullvadvpn.util.Lc +import net.mullvad.mullvadvpn.util.isDaitaDirectOnly +import net.mullvad.mullvadvpn.util.isDaitaEnabled import net.mullvad.mullvadvpn.util.toLc class DaitaViewModel( @@ -27,8 +28,8 @@ class DaitaViewModel( .filterNotNull() .map { settings -> DaitaUiState( - daitaEnabled = settings.daitaSettings().enabled, - directOnly = settings.daitaSettings().directOnly, + daitaEnabled = settings.isDaitaEnabled(), + directOnly = settings.isDaitaDirectOnly(), navArgs.isModal, ) .toLc<Boolean, DaitaUiState>() @@ -46,6 +47,4 @@ class DaitaViewModel( fun setDirectOnly(enable: Boolean) { viewModelScope.launch { settingsRepository.setDaitaDirectOnly(enable) } } - - private fun Settings.daitaSettings() = tunnelOptions.wireguard.daitaSettings } 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 c79682039b..d5d733e8da 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 @@ -24,6 +24,7 @@ import kotlinx.coroutines.launch import net.mullvad.mullvadvpn.lib.model.Settings import net.mullvad.mullvadvpn.repository.SettingsRepository import net.mullvad.mullvadvpn.usecase.DeleteCustomDnsUseCase +import net.mullvad.mullvadvpn.util.customDnsAddresses import org.apache.commons.validator.routines.InetAddressValidator sealed interface DnsDialogSideEffect { @@ -80,7 +81,9 @@ class DnsDialogViewModel( DnsDialogViewState( input = input, validationError = - input.validateDnsEntry(currentIndex, settings.addresses()).leftOrNull(), + input + .validateDnsEntry(currentIndex, settings.customDnsAddresses()) + .leftOrNull(), isAllowLanEnabled = settings.allowLan, isIpv6Enabled = settings.tunnelOptions.genericOptions.enableIpv6, index = currentIndex, @@ -169,8 +172,6 @@ class DnsDialogViewModel( } } - private fun Settings.addresses() = tunnelOptions.dnsOptions.customOptions.addresses - companion object { private const val EMPTY_STRING = "" } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/ShadowsocksSettingsViewModel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/ShadowsocksSettingsViewModel.kt index fa0e886fee..c7c9e8d900 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/ShadowsocksSettingsViewModel.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/ShadowsocksSettingsViewModel.kt @@ -16,9 +16,9 @@ import net.mullvad.mullvadvpn.compose.state.ShadowsocksSettingsUiState import net.mullvad.mullvadvpn.constant.SHADOWSOCKS_PRESET_PORTS import net.mullvad.mullvadvpn.lib.model.Constraint import net.mullvad.mullvadvpn.lib.model.Port -import net.mullvad.mullvadvpn.lib.model.Settings import net.mullvad.mullvadvpn.repository.SettingsRepository import net.mullvad.mullvadvpn.util.Lc +import net.mullvad.mullvadvpn.util.shadowSocksPort import net.mullvad.mullvadvpn.util.toLc class ShadowsocksSettingsViewModel(private val settingsRepository: SettingsRepository) : @@ -31,7 +31,7 @@ class ShadowsocksSettingsViewModel(private val settingsRepository: SettingsRepos settings, customPort -> ShadowsocksSettingsUiState( - port = settings.getShadowSocksPort(), + port = settings.shadowSocksPort(), customPort = customPort, ) .toLc<Unit, ShadowsocksSettingsUiState>() @@ -46,7 +46,7 @@ class ShadowsocksSettingsViewModel(private val settingsRepository: SettingsRepos viewModelScope.launch { val initialSettings = settingsRepository.settingsUpdates.filterNotNull().first() customPort.update { - val initialPort = initialSettings.getShadowSocksPort() + val initialPort = initialSettings.shadowSocksPort() if (initialPort.getOrNull() !in SHADOWSOCKS_PRESET_PORTS) { initialPort.getOrNull() } else { @@ -79,6 +79,4 @@ class ShadowsocksSettingsViewModel(private val settingsRepository: SettingsRepos } } } - - private fun Settings.getShadowSocksPort() = obfuscationSettings.shadowsocks.port } 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 3f9a727b38..9ba3e00995 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 @@ -33,15 +33,21 @@ import net.mullvad.mullvadvpn.lib.model.IpVersion import net.mullvad.mullvadvpn.lib.model.ObfuscationMode import net.mullvad.mullvadvpn.lib.model.Port import net.mullvad.mullvadvpn.lib.model.QuantumResistantState -import net.mullvad.mullvadvpn.lib.model.Settings import net.mullvad.mullvadvpn.repository.AutoStartAndConnectOnBootRepository import net.mullvad.mullvadvpn.repository.RelayListRepository import net.mullvad.mullvadvpn.repository.SettingsRepository import net.mullvad.mullvadvpn.repository.WireguardConstraintsRepository import net.mullvad.mullvadvpn.usecase.SystemVpnSettingsAvailableUseCase import net.mullvad.mullvadvpn.util.Lc +import net.mullvad.mullvadvpn.util.contentBlockersSettings +import net.mullvad.mullvadvpn.util.customDnsAddresses +import net.mullvad.mullvadvpn.util.deviceIpVersion +import net.mullvad.mullvadvpn.util.isCustomDnsEnabled import net.mullvad.mullvadvpn.util.onFirst +import net.mullvad.mullvadvpn.util.quantumResistant +import net.mullvad.mullvadvpn.util.selectedObfuscationMode import net.mullvad.mullvadvpn.util.toLc +import net.mullvad.mullvadvpn.util.wireguardPort sealed interface VpnSettingsSideEffect { sealed interface ShowToast : VpnSettingsSideEffect { @@ -75,7 +81,7 @@ class VpnSettingsViewModel( combine( settingsRepository.settingsUpdates.filterNotNull().onFirst { // Initialize wg port and content blockers state expand state - val initialPort = it.getWireguardPort().getOrNull() + val initialPort = it.wireguardPort().getOrNull() customPort.value = Some( if (initialPort !in WIREGUARD_PRESET_PORTS) { @@ -101,19 +107,19 @@ class VpnSettingsViewModel( mtu = settings.tunnelOptions.wireguard.mtu, isLocalNetworkSharingEnabled = settings.allowLan, isCustomDnsEnabled = settings.isCustomDnsEnabled(), - customDnsItems = settings.addresses().asStringAddressList(), + customDnsItems = settings.customDnsAddresses().asStringAddressList(), contentBlockersOptions = settings.contentBlockersSettings(), obfuscationMode = settings.selectedObfuscationMode(), selectedUdp2TcpObfuscationPort = settings.obfuscationSettings.udp2tcp.port, selectedShadowsocksObfuscationPort = settings.obfuscationSettings.shadowsocks.port, quantumResistant = settings.quantumResistant(), - selectedWireguardPort = settings.getWireguardPort(), + selectedWireguardPort = settings.wireguardPort(), customWireguardPort = customWgPort, availablePortRanges = portRanges, systemVpnSettingsAvailable = systemVpnSettingsUseCase(), autoStartAndConnectOnBoot = autoStartAndConnectOnBoot, - deviceIpVersion = settings.getDeviceIpVersion(), + deviceIpVersion = settings.deviceIpVersion(), isIpv6Enabled = settings.tunnelOptions.genericOptions.enableIpv6, isContentBlockersExpanded = isContentBlockersExpanded, isModal = navArgs.isModal, @@ -138,7 +144,7 @@ class VpnSettingsViewModel( return@launch } - val hasDnsEntries = settings.addresses().isNotEmpty() + val hasDnsEntries = settings.customDnsAddresses().isNotEmpty() if (hasDnsEntries) { settingsRepository @@ -258,22 +264,6 @@ class VpnSettingsViewModel( ) } - private fun Settings.quantumResistant() = tunnelOptions.wireguard.quantumResistant - - private fun Settings.isCustomDnsEnabled() = tunnelOptions.dnsOptions.state == DnsState.Custom - - private fun Settings.addresses() = tunnelOptions.dnsOptions.customOptions.addresses - - private fun Settings.contentBlockersSettings() = tunnelOptions.dnsOptions.defaultOptions - - private fun Settings.selectedObfuscationMode() = obfuscationSettings.selectedObfuscationMode - - private fun Settings.getWireguardPort() = - relaySettings.relayConstraints.wireguardConstraints.port - - private fun Settings.getDeviceIpVersion() = - relaySettings.relayConstraints.wireguardConstraints.ipVersion - private fun InetAddress.isLocalAddress(): Boolean = isLinkLocalAddress || isSiteLocalAddress fun showApplySettingChangesWarningToast() = diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/location/LocationUtil.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/location/LocationUtil.kt new file mode 100644 index 0000000000..2561f7f007 --- /dev/null +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/location/LocationUtil.kt @@ -0,0 +1,26 @@ +package net.mullvad.mullvadvpn.viewmodel.location + +import net.mullvad.mullvadvpn.compose.state.MultihopRelayListType +import net.mullvad.mullvadvpn.compose.state.RelayListType +import net.mullvad.mullvadvpn.lib.model.Settings +import net.mullvad.mullvadvpn.util.isDaitaDirectOnly +import net.mullvad.mullvadvpn.util.isDaitaEnabled +import net.mullvad.mullvadvpn.util.isMultihopEnabled + +// If Daita is enabled without direct only we should block selection, search and hide filters for +// the multihop enry list +internal fun RelayListType.isEntryAndBlocked(settings: Settings?): Boolean { + val isMultihopEntry = isMultihopEntry() + + if (!isMultihopEntry) { + return false + } + + return settings?.entryBlocked() == true +} + +private fun Settings.entryBlocked() = + isDaitaEnabled() && !isDaitaDirectOnly() && isMultihopEnabled() + +private fun RelayListType.isMultihopEntry() = + this is RelayListType.Multihop && multihopRelayListType == MultihopRelayListType.ENTRY diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/location/RelayItemListCreator.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/location/RelayItemListCreator.kt index 1d6691b755..d1c8a6ba61 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/location/RelayItemListCreator.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/location/RelayItemListCreator.kt @@ -121,17 +121,8 @@ private fun createRecentsSection( ): List<RelayListItem> = buildList { add(RelayListItem.RecentsListHeader) - val selectionIsSingle = itemSelection is RelayItemSelection.Single - val selectionIsMulti = itemSelection is RelayItemSelection.Multiple - val shown = recents - .filter { recent -> - when (recent) { - is Hop.Multi -> selectionIsMulti - is Hop.Single<*> -> selectionIsSingle - } - } .map { recent -> val isSelected = recent.matches(itemSelection, isEntryBlocked) if (isEntryBlocked) { diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/location/SelectLocationListViewModel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/location/SelectLocationListViewModel.kt index 30e45d05cf..ff67946361 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/location/SelectLocationListViewModel.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/location/SelectLocationListViewModel.kt @@ -13,7 +13,6 @@ import net.mullvad.mullvadvpn.compose.state.SelectLocationListUiState import net.mullvad.mullvadvpn.lib.model.CustomListId import net.mullvad.mullvadvpn.lib.model.GeoLocationId import net.mullvad.mullvadvpn.lib.model.RelayItemId -import net.mullvad.mullvadvpn.lib.model.Settings import net.mullvad.mullvadvpn.repository.RelayListRepository import net.mullvad.mullvadvpn.repository.SettingsRepository import net.mullvad.mullvadvpn.repository.WireguardConstraintsRepository @@ -44,7 +43,7 @@ class SelectLocationListViewModel( customListsRelayItemUseCase(), settingsRepository.settingsUpdates, ) { relayListItems, customLists, settings -> - if (settings.isBlocked()) { + if (relayListType.isEntryAndBlocked(settings)) { Lce.Error(Unit) } else { Lce.Content( @@ -62,19 +61,11 @@ class SelectLocationListViewModel( _expandedItems.onToggleExpandSet(item, parent, expand) } - private fun Settings?.isBlocked(): Boolean = - when (relayListType) { - RelayListType.Single -> false - is RelayListType.Multihop -> - relayListType.multihopRelayListType == MultihopRelayListType.ENTRY && - this?.entryBlocked() == true - } - private fun relayListItems() = combine( filteredRelayListUseCase(relayListType = relayListType), filteredCustomListRelayItemsUseCase(relayListType = relayListType), - recentsUseCase(), + recentsUseCase(isMultihop = relayListType is RelayListType.Multihop), selectedLocationUseCase(), _expandedItems, ) { relayCountries, customLists, recents, selectedItem, expandedItems -> @@ -103,7 +94,7 @@ class SelectLocationListViewModel( selectedItem.selectedByOtherEntryExitList(relayListType, customLists), expandedItems = expandedItems, isEntryBlocked = - settingsRepository.settingsUpdates.value?.entryBlocked() == true, + relayListType.isEntryAndBlocked(settingsRepository.settingsUpdates.value), ) } } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/location/SelectLocationViewModel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/location/SelectLocationViewModel.kt index 768cf794eb..bf3abeaf91 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/location/SelectLocationViewModel.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/location/SelectLocationViewModel.kt @@ -21,6 +21,7 @@ import net.mullvad.mullvadvpn.lib.model.CustomListId import net.mullvad.mullvadvpn.lib.model.Hop import net.mullvad.mullvadvpn.lib.model.Recents import net.mullvad.mullvadvpn.lib.model.RelayItem +import net.mullvad.mullvadvpn.lib.model.Settings import net.mullvad.mullvadvpn.repository.CustomListsRepository import net.mullvad.mullvadvpn.repository.RelayListFilterRepository import net.mullvad.mullvadvpn.repository.RelayListRepository @@ -69,13 +70,20 @@ class SelectLocationViewModel( ) { filterChips, wireguardConstraints, relayListSelection, relayList, settings -> Lc.Content( SelectLocationUiState( - filterChips = filterChips, + filterChips = + // Hide filter chips when entry and blocked + if (relayListSelection.isEntryAndBlocked(settings)) { + emptyList() + } else { + filterChips + }, multihopEnabled = wireguardConstraints?.isMultihopEnabled == true, relayListType = relayListSelection, isSearchButtonEnabled = searchButtonEnabled( relayList = relayList, relayListSelection = relayListSelection, + settings = settings, ), isFilterButtonEnabled = relayList.isNotEmpty(), isRecentsEnabled = settings?.recents is Recents.Enabled, @@ -92,13 +100,11 @@ class SelectLocationViewModel( private fun searchButtonEnabled( relayList: List<RelayItem.Location.Country>, relayListSelection: RelayListType, + settings: Settings?, ): Boolean { val hasRelayListItems = relayList.isNotEmpty() - val isMultihopEntry = - relayListSelection is RelayListType.Multihop && - relayListSelection.multihopRelayListType == MultihopRelayListType.ENTRY - val isEntryBlocked = settingsRepository.settingsUpdates.value?.entryBlocked() == true - return hasRelayListItems && !(isMultihopEntry && isEntryBlocked) + val isEntryAndBlocked = relayListSelection.isEntryAndBlocked(settings = settings) + return hasRelayListItems && !isEntryAndBlocked } fun selectRelayList(multihopRelayListType: MultihopRelayListType) { diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/location/SettingsUtil.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/location/SettingsUtil.kt deleted file mode 100644 index 05245f2503..0000000000 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/location/SettingsUtil.kt +++ /dev/null @@ -1,10 +0,0 @@ -package net.mullvad.mullvadvpn.viewmodel.location - -import net.mullvad.mullvadvpn.lib.model.Settings - -// If Daita is enabled without direct only, it is not possible to manually select the entry -// location. -internal fun Settings.entryBlocked() = - tunnelOptions.wireguard.daitaSettings.enabled && - !tunnelOptions.wireguard.daitaSettings.directOnly && - relaySettings.relayConstraints.wireguardConstraints.isMultihopEnabled |
