summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorJonatan Rhodin <jonatan.rhodin@mullvad.net>2023-10-26 14:44:03 +0200
committerJonatan Rhodin <jonatan.rhodin@mullvad.net>2023-11-08 08:55:59 +0100
commit37353fb8909445192d2cd07bd9868a0186f6cf08 (patch)
tree88fcd78a610c0006fe2a35451efefbc99939cb55
parentec1731174cf0e2ee010979c37be3338aac266357 (diff)
downloadmullvadvpn-37353fb8909445192d2cd07bd9868a0186f6cf08.tar.xz
mullvadvpn-37353fb8909445192d2cd07bd9868a0186f6cf08.zip
Enable the relay list to be filtered by ownership and provider
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/di/UiModule.kt13
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayList.kt3
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayListExtensions.kt37
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/RelayListListener.kt137
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/ServiceConnectionContainer.kt2
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/ServiceConnectionManager.kt3
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/ServiceConnectionManagerExtensions.kt3
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/PortRangeUseCase.kt14
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/RelayListUseCase.kt61
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/ConnectViewModel.kt12
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/SelectLocationViewModel.kt54
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/VpnSettingsViewModel.kt77
-rw-r--r--android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/RelayListListener.kt3
13 files changed, 185 insertions, 234 deletions
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/di/UiModule.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/di/UiModule.kt
index 398e27820e..bfd3f061d5 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/di/UiModule.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/di/UiModule.kt
@@ -18,10 +18,13 @@ import net.mullvad.mullvadvpn.repository.InAppNotificationController
import net.mullvad.mullvadvpn.repository.PrivacyDisclaimerRepository
import net.mullvad.mullvadvpn.repository.SettingsRepository
import net.mullvad.mullvadvpn.ui.serviceconnection.MessageHandler
+import net.mullvad.mullvadvpn.ui.serviceconnection.RelayListListener
import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnectionManager
import net.mullvad.mullvadvpn.ui.serviceconnection.SplitTunneling
import net.mullvad.mullvadvpn.usecase.AccountExpiryNotificationUseCase
import net.mullvad.mullvadvpn.usecase.NewDeviceNotificationUseCase
+import net.mullvad.mullvadvpn.usecase.PortRangeUseCase
+import net.mullvad.mullvadvpn.usecase.RelayListUseCase
import net.mullvad.mullvadvpn.usecase.TunnelStateNotificationUseCase
import net.mullvad.mullvadvpn.usecase.VersionNotificationUseCase
import net.mullvad.mullvadvpn.util.ChangelogDataProvider
@@ -88,25 +91,29 @@ val uiModule = module {
single { TunnelStateNotificationUseCase(get()) }
single { VersionNotificationUseCase(get(), BuildConfig.ENABLE_IN_APP_VERSION_NOTIFICATIONS) }
single { NewDeviceNotificationUseCase(get()) }
+ single { PortRangeUseCase(get()) }
+ single { RelayListUseCase(get(), get()) }
single { InAppNotificationController(get(), get(), get(), get(), MainScope()) }
single<IChangelogDataProvider> { ChangelogDataProvider(get()) }
+ single { RelayListListener(get()) }
+
// View models
viewModel { AccountViewModel(get(), get(), get()) }
viewModel {
ChangelogViewModel(get(), BuildConfig.VERSION_CODE, BuildConfig.ALWAYS_SHOW_CHANGELOG)
}
- viewModel { ConnectViewModel(get(), get(), get(), get(), get()) }
+ viewModel { ConnectViewModel(get(), get(), get(), get(), get(), get()) }
viewModel { DeviceListViewModel(get(), get()) }
viewModel { DeviceRevokedViewModel(get(), get()) }
viewModel { LoginViewModel(get(), get(), get()) }
viewModel { PrivacyDisclaimerViewModel(get()) }
- viewModel { SelectLocationViewModel(get()) }
+ viewModel { SelectLocationViewModel(get(), get()) }
viewModel { SettingsViewModel(get(), get()) }
viewModel { VoucherDialogViewModel(get(), get()) }
- viewModel { VpnSettingsViewModel(get(), get(), get(), get()) }
+ viewModel { VpnSettingsViewModel(get(), get(), get(), get(), get()) }
viewModel { WelcomeViewModel(get(), get(), get()) }
viewModel { ReportProblemViewModel(get()) }
viewModel { ViewLogsViewModel(get()) }
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayList.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayList.kt
new file mode 100644
index 0000000000..c74c74ba43
--- /dev/null
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayList.kt
@@ -0,0 +1,3 @@
+package net.mullvad.mullvadvpn.relaylist
+
+data class RelayList(val country: List<RelayCountry>, val selectedItem: RelayItem?)
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayListExtensions.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayListExtensions.kt
index 39618f9603..71d4701e41 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayListExtensions.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayListExtensions.kt
@@ -2,14 +2,20 @@ package net.mullvad.mullvadvpn.relaylist
import net.mullvad.mullvadvpn.model.Constraint
import net.mullvad.mullvadvpn.model.GeographicLocationConstraint
+import net.mullvad.mullvadvpn.model.Ownership
+import net.mullvad.mullvadvpn.model.Providers
+import net.mullvad.mullvadvpn.model.Relay as DaemonRelay
import net.mullvad.mullvadvpn.model.RelayList
/**
* Convert from a model.RelayList to list of relaylist.RelayCountry Non-wiregaurd relays are
- * filtered out So are also cities that only contains non-wireguard relays Countries, cities and
- * relays are ordered by name
+ * filtered out and also relays that do not fit the ownership and provider list So are also cities
+ * that only contains non-wireguard relays Countries, cities and relays are ordered by name
*/
-fun RelayList.toRelayCountries(): List<RelayCountry> {
+fun RelayList.toRelayCountries(
+ ownership: Constraint<Ownership>,
+ providers: Constraint<Providers>
+): List<RelayCountry> {
val relayCountries =
this.countries
.map { country ->
@@ -27,7 +33,8 @@ fun RelayList.toRelayCountries(): List<RelayCountry> {
relays = relays
)
- val validCityRelays = city.relays.filter { relay -> relay.isWireguardRelay }
+ val validCityRelays =
+ city.relays.filterValidRelays(ownership = ownership, providers = providers)
for (relay in validCityRelays) {
relays.add(
@@ -170,6 +177,28 @@ fun List<RelayCountry>.filterOnSearchTerm(
}
}
+private fun List<DaemonRelay>.filterValidRelays(
+ ownership: Constraint<Ownership>,
+ providers: Constraint<Providers>
+): List<DaemonRelay> =
+ filter { it.isWireguardRelay }
+ .filter {
+ when (ownership) {
+ is Constraint.Any -> true
+ is Constraint.Only ->
+ when (ownership.value) {
+ Ownership.MullvadOwned -> it.owned
+ Ownership.Rented -> !it.owned
+ }
+ }
+ }
+ .filter { relay ->
+ when (providers) {
+ is Constraint.Any -> true
+ is Constraint.Only -> providers.value.providers.contains(relay.provider)
+ }
+ }
+
/** Expand the parent(s), if any, for the current selected item */
private fun List<RelayCountry>.expandItemForSelection(
selectedItem: RelayItem?
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/RelayListListener.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/RelayListListener.kt
index 5c6e765b4e..7f316bacb8 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/RelayListListener.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/RelayListListener.kt
@@ -1,135 +1,36 @@
package net.mullvad.mullvadvpn.ui.serviceconnection
-import android.os.Messenger
-import net.mullvad.mullvadvpn.lib.common.util.toGeographicLocationConstraint
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
import net.mullvad.mullvadvpn.lib.ipc.Event
-import net.mullvad.mullvadvpn.lib.ipc.EventDispatcher
import net.mullvad.mullvadvpn.lib.ipc.Request
-import net.mullvad.mullvadvpn.model.Constraint
import net.mullvad.mullvadvpn.model.GeographicLocationConstraint
-import net.mullvad.mullvadvpn.model.PortRange
-import net.mullvad.mullvadvpn.model.RelayConstraints
-import net.mullvad.mullvadvpn.model.RelaySettings
+import net.mullvad.mullvadvpn.model.RelayList
import net.mullvad.mullvadvpn.model.WireguardConstraints
-import net.mullvad.mullvadvpn.relaylist.RelayCountry
-import net.mullvad.mullvadvpn.relaylist.RelayItem
-import net.mullvad.mullvadvpn.relaylist.findItemForLocation
-import net.mullvad.mullvadvpn.relaylist.toRelayCountries
+import net.mullvad.mullvadvpn.model.WireguardEndpointData
class RelayListListener(
- private val connection: Messenger,
- eventDispatcher: EventDispatcher,
- private val settingsListener: SettingsListener
+ private val messageHandler: MessageHandler,
+ dispatcher: CoroutineDispatcher = Dispatchers.IO
) {
- private var relayCountries: List<RelayCountry>? = null
- private var relaySettings: RelaySettings? = null
- private var portRanges: List<PortRange> = emptyList()
-
- var selectedRelayItem: RelayItem? = null
- private set
+ val relayListEvents: StateFlow<RelayList> =
+ messageHandler
+ .events<Event.NewRelayList>()
+ .map { it.relayList ?: defaultRelayList() }
+ .stateIn(CoroutineScope(dispatcher), SharingStarted.Eagerly, defaultRelayList())
fun updateSelectedRelayLocation(value: GeographicLocationConstraint) {
- connection.send(Request.SetRelayLocation(value).message)
+ messageHandler.trySendRequest(Request.SetRelayLocation(value))
}
fun updateSelectedWireguardConstraints(value: WireguardConstraints) {
- connection.send(Request.SetWireguardConstraints(value).message)
- }
-
- var onRelayCountriesChange: ((List<RelayCountry>, RelayItem?) -> Unit)? = null
- set(value) {
- field = value
-
- synchronized(this) {
- val relayCountries = this.relayCountries
-
- if (relayCountries != null) {
- value?.invoke(relayCountries, selectedRelayItem)
- }
- }
- }
-
- var onPortRangesChange: ((List<PortRange>) -> Unit)? = null
- set(value) {
- field = value
-
- synchronized(this) { value?.invoke(portRanges) }
- }
-
- init {
- eventDispatcher.registerHandler(Event.NewRelayList::class) { event ->
- event.relayList?.let { relayLocations ->
- relayListChanged(relayLocations.toRelayCountries())
- portRangesChanged(relayLocations.wireguardEndpointData.portRanges)
- }
- }
-
- settingsListener.relaySettingsNotifier.subscribe(this) { newRelaySettings ->
- relaySettingsChanged(newRelaySettings)
- }
- }
-
- fun onDestroy() {
- settingsListener.relaySettingsNotifier.unsubscribe(this)
- onRelayCountriesChange = null
- }
-
- private fun relaySettingsChanged(newRelaySettings: RelaySettings?) {
- synchronized(this) {
- val relayCountries = this.relayCountries
- val portRanges = this.portRanges
-
- relaySettings =
- newRelaySettings
- ?: RelaySettings.Normal(
- RelayConstraints(
- location = Constraint.Any(),
- ownership = Constraint.Any(),
- wireguardConstraints = WireguardConstraints(Constraint.Any()),
- providers = Constraint.Any()
- )
- )
-
- if (relayCountries != null) {
- relayListChanged(relayCountries)
- }
- portRangesChanged(portRanges)
- }
- }
-
- private fun relayListChanged(newRelayCountries: List<RelayCountry>) {
- synchronized(this) {
- relayCountries = newRelayCountries
- selectedRelayItem = findSelectedRelayItem()
-
- onRelayCountriesChange?.invoke(newRelayCountries, selectedRelayItem)
- }
+ messageHandler.trySendRequest(Request.SetWireguardConstraints(value))
}
- private fun portRangesChanged(newPortRanges: List<PortRange>) {
- synchronized(this) {
- portRanges = newPortRanges
-
- onPortRangesChange?.invoke(portRanges)
- }
- }
-
- private fun findSelectedRelayItem(): RelayItem? {
- val relaySettings = this.relaySettings
-
- when (relaySettings) {
- is RelaySettings.CustomTunnelEndpoint -> return null
- is RelaySettings.Normal -> {
- val location = relaySettings.relayConstraints.location
- return relayCountries?.findItemForLocation(
- location.toGeographicLocationConstraint()
- )
- }
- else -> {
- /* NOOP */
- }
- }
-
- return null
- }
+ private fun defaultRelayList() = RelayList(ArrayList(), WireguardEndpointData(ArrayList()))
}
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/ServiceConnectionContainer.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/ServiceConnectionContainer.kt
index 7d34f2a96f..ca156bed66 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/ServiceConnectionContainer.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/ServiceConnectionContainer.kt
@@ -37,7 +37,6 @@ class ServiceConnectionContainer(
val appVersionInfoCache = AppVersionInfoCache(dispatcher, settingsListener)
val customDns = CustomDns(connection)
- var relayListListener = RelayListListener(connection, dispatcher, settingsListener)
private var listenerId: Int? = null
@@ -68,7 +67,6 @@ class ServiceConnectionContainer(
voucherRedeemer.onDestroy()
appVersionInfoCache.onDestroy()
- relayListListener.onDestroy()
}
private fun registerListener(connection: Messenger) {
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/ServiceConnectionManager.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/ServiceConnectionManager.kt
index d840b93491..556d69ecfe 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/ServiceConnectionManager.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/ServiceConnectionManager.kt
@@ -12,6 +12,7 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.filterIsInstance
+import kotlinx.coroutines.flow.map
import net.mullvad.mullvadvpn.lib.endpoint.ApiEndpointConfiguration
import net.mullvad.mullvadvpn.lib.endpoint.BuildConfig
import net.mullvad.mullvadvpn.lib.endpoint.putApiEndpointConfigurationExtra
@@ -93,7 +94,7 @@ class ServiceConnectionManager(private val context: Context) : MessageHandler {
}
override fun <E : Event> events(klass: KClass<E>): Flow<E> {
- return events.filterIsInstance(klass)
+ return events.map { it }.filterIsInstance(klass)
}
override fun trySendRequest(request: Request): Boolean {
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/ServiceConnectionManagerExtensions.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/ServiceConnectionManagerExtensions.kt
index c4b3d100bd..3232d20cbd 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/ServiceConnectionManagerExtensions.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/ServiceConnectionManagerExtensions.kt
@@ -14,9 +14,6 @@ fun ServiceConnectionManager.deviceDataSource() =
fun ServiceConnectionManager.customDns() = this.connectionState.value.readyContainer()?.customDns
-fun ServiceConnectionManager.relayListListener() =
- this.connectionState.value.readyContainer()?.relayListListener
-
fun ServiceConnectionManager.settingsListener() =
this.connectionState.value.readyContainer()?.settingsListener
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/PortRangeUseCase.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/PortRangeUseCase.kt
new file mode 100644
index 0000000000..2b104cda39
--- /dev/null
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/PortRangeUseCase.kt
@@ -0,0 +1,14 @@
+package net.mullvad.mullvadvpn.usecase
+
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.map
+import net.mullvad.mullvadvpn.model.PortRange
+import net.mullvad.mullvadvpn.ui.serviceconnection.RelayListListener
+
+class PortRangeUseCase(private val relayListListener: RelayListListener) {
+ fun portRanges(): Flow<List<PortRange>> =
+ relayListListener.relayListEvents
+ .map { it?.wireguardEndpointData?.portRanges ?: emptyList() }
+ .distinctUntilChanged()
+}
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/RelayListUseCase.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/RelayListUseCase.kt
new file mode 100644
index 0000000000..37dce1dda5
--- /dev/null
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/RelayListUseCase.kt
@@ -0,0 +1,61 @@
+package net.mullvad.mullvadvpn.usecase
+
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.map
+import net.mullvad.mullvadvpn.lib.common.util.toGeographicLocationConstraint
+import net.mullvad.mullvadvpn.model.Constraint
+import net.mullvad.mullvadvpn.model.GeographicLocationConstraint
+import net.mullvad.mullvadvpn.model.RelayConstraints
+import net.mullvad.mullvadvpn.model.RelaySettings
+import net.mullvad.mullvadvpn.model.WireguardConstraints
+import net.mullvad.mullvadvpn.relaylist.RelayCountry
+import net.mullvad.mullvadvpn.relaylist.RelayItem
+import net.mullvad.mullvadvpn.relaylist.RelayList
+import net.mullvad.mullvadvpn.relaylist.findItemForLocation
+import net.mullvad.mullvadvpn.relaylist.toRelayCountries
+import net.mullvad.mullvadvpn.repository.SettingsRepository
+import net.mullvad.mullvadvpn.ui.serviceconnection.RelayListListener
+
+class RelayListUseCase(
+ private val relayListListener: RelayListListener,
+ private val settingsRepository: SettingsRepository
+) {
+
+ fun updateSelectedRelayLocation(value: GeographicLocationConstraint) {
+ relayListListener.updateSelectedRelayLocation(value)
+ }
+
+ fun updateSelectedWireguardConstraints(value: WireguardConstraints) {
+ relayListListener.updateSelectedWireguardConstraints(value)
+ }
+
+ fun relayListWithSelection(): Flow<RelayList> =
+ combine(relayListListener.relayListEvents, settingsRepository.settingsUpdates) {
+ relayList,
+ settings ->
+ val ownership =
+ settings?.relaySettings?.relayConstraints()?.ownership ?: Constraint.Any()
+ val providers =
+ settings?.relaySettings?.relayConstraints()?.providers ?: Constraint.Any()
+ val relayCountries =
+ relayList.toRelayCountries(ownership = ownership, providers = providers)
+ val selectedItem =
+ relayCountries.findSelectedRelayItem(
+ relaySettings = settings?.relaySettings,
+ )
+ RelayList(relayCountries, selectedItem)
+ }
+
+ fun selectedRelayItem(): Flow<RelayItem?> = relayListWithSelection().map { it.selectedItem }
+
+ private fun List<RelayCountry>.findSelectedRelayItem(
+ relaySettings: RelaySettings?,
+ ): RelayItem? {
+ val location = relaySettings?.relayConstraints()?.location
+ return location?.let { this.findItemForLocation(location.toGeographicLocationConstraint()) }
+ }
+
+ private fun RelaySettings.relayConstraints(): RelayConstraints? =
+ (this as? RelaySettings.Normal)?.relayConstraints
+}
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/ConnectViewModel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/ConnectViewModel.kt
index 8a4f087d64..9f6cec8391 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/ConnectViewModel.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/ConnectViewModel.kt
@@ -27,13 +27,13 @@ import net.mullvad.mullvadvpn.repository.DeviceRepository
import net.mullvad.mullvadvpn.repository.InAppNotificationController
import net.mullvad.mullvadvpn.ui.serviceconnection.ConnectionProxy
import net.mullvad.mullvadvpn.ui.serviceconnection.LocationInfoCache
-import net.mullvad.mullvadvpn.ui.serviceconnection.RelayListListener
import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnectionContainer
import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnectionManager
import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnectionState
import net.mullvad.mullvadvpn.ui.serviceconnection.authTokenCache
import net.mullvad.mullvadvpn.ui.serviceconnection.connectionProxy
import net.mullvad.mullvadvpn.usecase.NewDeviceNotificationUseCase
+import net.mullvad.mullvadvpn.usecase.RelayListUseCase
import net.mullvad.mullvadvpn.util.callbackFlowFromNotifier
import net.mullvad.mullvadvpn.util.combine
import net.mullvad.mullvadvpn.util.daysFromNow
@@ -48,7 +48,8 @@ class ConnectViewModel(
accountRepository: AccountRepository,
private val deviceRepository: DeviceRepository,
private val inAppNotificationController: InAppNotificationController,
- private val newDeviceNotificationUseCase: NewDeviceNotificationUseCase
+ private val newDeviceNotificationUseCase: NewDeviceNotificationUseCase,
+ private val relayListUseCase: RelayListUseCase
) : ViewModel() {
private val _uiSideEffect = MutableSharedFlow<UiSideEffect>(extraBufferCapacity = 1)
val uiSideEffect = _uiSideEffect.asSharedFlow()
@@ -71,7 +72,7 @@ class ConnectViewModel(
.flatMapLatest { serviceConnection ->
combine(
serviceConnection.locationInfoCache.locationCallbackFlow(),
- serviceConnection.relayListListener.relayListCallbackFlow(),
+ relayListUseCase.selectedRelayItem(),
inAppNotificationController.notifications,
serviceConnection.connectionProxy.tunnelUiStateFlow(),
serviceConnection.connectionProxy.tunnelRealStateFlow(),
@@ -137,11 +138,6 @@ class ConnectViewModel(
awaitClose { onNewLocation = null }
}
- private fun RelayListListener.relayListCallbackFlow() = callbackFlow {
- onRelayCountriesChange = { _, item -> this.trySend(item) }
- awaitClose { onRelayCountriesChange = null }
- }
-
private fun ConnectionProxy.tunnelUiStateFlow(): Flow<TunnelState> =
callbackFlowFromNotifier(this.onUiStateChange)
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/SelectLocationViewModel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/SelectLocationViewModel.kt
index 0d8f753d8f..5e95674e0a 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/SelectLocationViewModel.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/SelectLocationViewModel.kt
@@ -2,60 +2,39 @@ package net.mullvad.mullvadvpn.viewmodel
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
-import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.asSharedFlow
-import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.emptyFlow
-import kotlinx.coroutines.flow.flatMapLatest
-import kotlinx.coroutines.flow.flowOf
-import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import net.mullvad.mullvadvpn.compose.state.SelectLocationUiState
import net.mullvad.mullvadvpn.relaylist.RelayItem
import net.mullvad.mullvadvpn.relaylist.filterOnSearchTerm
-import net.mullvad.mullvadvpn.ui.serviceconnection.RelayListListener
import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnectionManager
-import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnectionState
import net.mullvad.mullvadvpn.ui.serviceconnection.connectionProxy
-import net.mullvad.mullvadvpn.ui.serviceconnection.relayListListener
+import net.mullvad.mullvadvpn.usecase.RelayListUseCase
-class SelectLocationViewModel(private val serviceConnectionManager: ServiceConnectionManager) :
- ViewModel() {
+class SelectLocationViewModel(
+ private val serviceConnectionManager: ServiceConnectionManager,
+ private val relayListUseCase: RelayListUseCase
+) : ViewModel() {
private val _closeAction = MutableSharedFlow<Unit>()
private val _enterTransitionEndAction = MutableSharedFlow<Unit>()
private val _searchTerm = MutableStateFlow(EMPTY_SEARCH_TERM)
val uiState =
- serviceConnectionManager.connectionState
- .flatMapLatest { state ->
- if (state is ServiceConnectionState.ConnectedReady) {
- flowOf(state.container)
- } else {
- emptyFlow()
- }
- }
- .flatMapLatest { serviceConnection ->
- combine(serviceConnection.relayListListener.relayListCallbackFlow(), _searchTerm) {
- (relayCountries, relayItem),
- searchTerm ->
- Triple(
- relayCountries.filterOnSearchTerm(searchTerm, relayItem),
- relayItem,
- searchTerm
- )
- }
- }
- .map { (relayCountries, relayItem, searchTerm) ->
- if (searchTerm.isNotEmpty() && relayCountries.isEmpty()) {
+ combine(relayListUseCase.relayListWithSelection(), _searchTerm) {
+ (relayCountries, relayItem),
+ searchTerm ->
+ val filteredRelayCountries =
+ relayCountries.filterOnSearchTerm(searchTerm, relayItem)
+ if (searchTerm.isNotEmpty() && filteredRelayCountries.isEmpty()) {
SelectLocationUiState.NoSearchResultFound(searchTerm = searchTerm)
} else {
SelectLocationUiState.ShowData(
- countries = relayCountries,
+ countries = filteredRelayCountries,
selectedRelay = relayItem
)
}
@@ -72,9 +51,7 @@ class SelectLocationViewModel(private val serviceConnectionManager: ServiceConne
val enterTransitionEndAction = _enterTransitionEndAction.asSharedFlow()
fun selectRelay(relayItem: RelayItem) {
- serviceConnectionManager
- .relayListListener()
- ?.updateSelectedRelayLocation(relayItem.location)
+ relayListUseCase.updateSelectedRelayLocation(relayItem.location)
serviceConnectionManager.connectionProxy()?.connect()
viewModelScope.launch { _closeAction.emit(Unit) }
}
@@ -87,11 +64,6 @@ class SelectLocationViewModel(private val serviceConnectionManager: ServiceConne
viewModelScope.launch { _searchTerm.emit(searchTerm) }
}
- private fun RelayListListener.relayListCallbackFlow() = callbackFlow {
- onRelayCountriesChange = { list, item -> this.trySend(list to item) }
- awaitClose { onRelayCountriesChange = null }
- }
-
companion object {
private const val EMPTY_SEARCH_TERM = ""
}
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 94abf1da90..dfae3df539 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/VpnSettingsViewModel.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/VpnSettingsViewModel.kt
@@ -7,16 +7,11 @@ import androidx.lifecycle.viewModelScope
import java.net.InetAddress
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.asSharedFlow
-import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.emptyFlow
-import kotlinx.coroutines.flow.flatMapLatest
-import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.update
@@ -35,10 +30,8 @@ import net.mullvad.mullvadvpn.model.Settings
import net.mullvad.mullvadvpn.model.Udp2TcpObfuscationSettings
import net.mullvad.mullvadvpn.model.WireguardConstraints
import net.mullvad.mullvadvpn.repository.SettingsRepository
-import net.mullvad.mullvadvpn.ui.serviceconnection.RelayListListener
-import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnectionManager
-import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnectionState
-import net.mullvad.mullvadvpn.ui.serviceconnection.relayListListener
+import net.mullvad.mullvadvpn.usecase.PortRangeUseCase
+import net.mullvad.mullvadvpn.usecase.RelayListUseCase
import net.mullvad.mullvadvpn.util.isValidMtu
import org.apache.commons.validator.routines.InetAddressValidator
@@ -46,7 +39,8 @@ class VpnSettingsViewModel(
private val repository: SettingsRepository,
private val inetAddressValidator: InetAddressValidator,
private val resources: Resources,
- private val serviceConnectionManager: ServiceConnectionManager,
+ portRangeUseCase: PortRangeUseCase,
+ private val relayListUseCase: RelayListUseCase,
private val dispatcher: CoroutineDispatcher = Dispatchers.IO
) : ViewModel() {
@@ -57,38 +51,26 @@ class VpnSettingsViewModel(
private val dialogState = MutableStateFlow<VpnSettingsDialogState?>(null)
private val vmState =
- serviceConnectionManager.connectionState
- .flatMapLatest { state ->
- if (state is ServiceConnectionState.ConnectedReady) {
- flowOf(state.container)
- } else {
- emptyFlow()
- }
- }
- .flatMapLatest { serviceConnection ->
- combine(
- repository.settingsUpdates,
- serviceConnection.relayListListener.portRangesCallbackFlow(),
- dialogState
- ) { settings, portRanges, dialogState ->
- VpnSettingsViewModelState(
- mtuValue = settings?.mtuString() ?: "",
- isAutoConnectEnabled = settings?.autoConnect ?: false,
- isLocalNetworkSharingEnabled = settings?.allowLan ?: false,
- isCustomDnsEnabled = settings?.isCustomDnsEnabled() ?: false,
- customDnsList = settings?.addresses()?.asStringAddressList() ?: listOf(),
- contentBlockersOptions = settings?.contentBlockersSettings()
- ?: DefaultDnsOptions(),
- isAllowLanEnabled = settings?.allowLan ?: false,
- selectedObfuscation = settings?.selectedObfuscationSettings()
- ?: SelectedObfuscation.Off,
- dialogState = dialogState,
- quantumResistant = settings?.quantumResistant()
- ?: QuantumResistantState.Off,
- selectedWireguardPort = settings?.getWireguardPort() ?: Constraint.Any(),
- availablePortRanges = portRanges
- )
- }
+ combine(repository.settingsUpdates, portRangeUseCase.portRanges(), dialogState) {
+ settings,
+ portRanges,
+ dialogState ->
+ VpnSettingsViewModelState(
+ mtuValue = settings?.mtuString() ?: "",
+ isAutoConnectEnabled = settings?.autoConnect ?: false,
+ isLocalNetworkSharingEnabled = settings?.allowLan ?: false,
+ isCustomDnsEnabled = settings?.isCustomDnsEnabled() ?: false,
+ customDnsList = settings?.addresses()?.asStringAddressList() ?: listOf(),
+ contentBlockersOptions = settings?.contentBlockersSettings()
+ ?: DefaultDnsOptions(),
+ isAllowLanEnabled = settings?.allowLan ?: false,
+ selectedObfuscation = settings?.selectedObfuscationSettings()
+ ?: SelectedObfuscation.Off,
+ dialogState = dialogState,
+ quantumResistant = settings?.quantumResistant() ?: QuantumResistantState.Off,
+ selectedWireguardPort = settings?.getWireguardPort() ?: Constraint.Any(),
+ availablePortRanges = portRanges
+ )
}
.stateIn(
viewModelScope,
@@ -351,11 +333,7 @@ class VpnSettingsViewModel(
}
fun onWireguardPortSelected(port: Constraint<Port>) {
- viewModelScope.launch(dispatcher) {
- serviceConnectionManager
- .relayListListener()
- ?.updateSelectedWireguardConstraints(WireguardConstraints(port = port))
- }
+ relayListUseCase.updateSelectedWireguardConstraints(WireguardConstraints(port = port))
hideDialog()
}
@@ -423,11 +401,6 @@ class VpnSettingsViewModel(
private fun Settings.selectedObfuscationSettings() = obfuscationSettings.selectedObfuscation
- private fun RelayListListener.portRangesCallbackFlow() = callbackFlow {
- onPortRangesChange = { portRanges -> this.trySend(portRanges) }
- awaitClose { onPortRangesChange = null }
- }
-
private fun Settings.getWireguardPort() =
when (relaySettings) {
RelaySettings.CustomTunnelEndpoint -> Constraint.Any()
diff --git a/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/RelayListListener.kt b/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/RelayListListener.kt
index 7a0b3fbe97..2fc1e4115a 100644
--- a/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/RelayListListener.kt
+++ b/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/RelayListListener.kt
@@ -91,8 +91,7 @@ class RelayListListener(endpoint: ServiceEndpoint) {
val location: Constraint<LocationConstraint> =
selectedRelayLocation?.let { location ->
Constraint.Only(LocationConstraint.Location(location))
- }
- ?: currentRelayConstraints.location
+ } ?: currentRelayConstraints.location
val wireguardConstraints: WireguardConstraints =
selectedWireguardConstraints ?: currentRelayConstraints.wireguardConstraints