diff options
Diffstat (limited to 'android')
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 |
