diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/ConnectScreenTest.kt b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/ConnectScreenTest.kt index 9ba5db81bcaa..435bbeab00de 100644 --- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/ConnectScreenTest.kt +++ b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/ConnectScreenTest.kt @@ -35,6 +35,7 @@ import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.RegisterExtension +@Suppress("LargeClass") class ConnectScreenTest { @OptIn(ExperimentalTestApi::class) @JvmField @@ -78,7 +79,7 @@ class ConnectScreenTest { ConnectUiState( location = null, selectedRelayItemTitle = null, - tunnelState = TunnelState.Connecting(null, null), + tunnelState = TunnelState.Connecting(null, null, emptyList()), inAddress = null, outAddress = "", showLocation = false, @@ -112,7 +113,11 @@ class ConnectScreenTest { location = null, selectedRelayItemTitle = null, tunnelState = - TunnelState.Connecting(endpoint = mockTunnelEndpoint, null), + TunnelState.Connecting( + endpoint = mockTunnelEndpoint, + null, + emptyList() + ), inAddress = null, outAddress = "", showLocation = false, @@ -144,7 +149,8 @@ class ConnectScreenTest { ConnectUiState( location = null, selectedRelayItemTitle = null, - tunnelState = TunnelState.Connected(mockTunnelEndpoint, null), + tunnelState = + TunnelState.Connected(mockTunnelEndpoint, null, emptyList()), inAddress = null, outAddress = "", showLocation = false, @@ -175,7 +181,8 @@ class ConnectScreenTest { ConnectUiState( location = null, selectedRelayItemTitle = null, - tunnelState = TunnelState.Connected(mockTunnelEndpoint, null), + tunnelState = + TunnelState.Connected(mockTunnelEndpoint, null, emptyList()), inAddress = null, outAddress = "", showLocation = false, @@ -437,7 +444,8 @@ class ConnectScreenTest { ConnectUiState( location = null, selectedRelayItemTitle = null, - tunnelState = TunnelState.Connected(mockTunnelEndpoint, null), + tunnelState = + TunnelState.Connected(mockTunnelEndpoint, null, emptyList()), inAddress = null, outAddress = "", showLocation = false, @@ -470,7 +478,8 @@ class ConnectScreenTest { ConnectUiState( location = null, selectedRelayItemTitle = null, - tunnelState = TunnelState.Connected(mockTunnelEndpoint, null), + tunnelState = + TunnelState.Connected(mockTunnelEndpoint, null, emptyList()), inAddress = null, outAddress = "", showLocation = false, @@ -534,7 +543,7 @@ class ConnectScreenTest { ConnectUiState( location = null, selectedRelayItemTitle = null, - tunnelState = TunnelState.Connecting(null, null), + tunnelState = TunnelState.Connecting(null, null, emptyList()), inAddress = null, outAddress = "", showLocation = false, @@ -574,7 +583,8 @@ class ConnectScreenTest { ConnectUiState( location = mockLocation, selectedRelayItemTitle = null, - tunnelState = TunnelState.Connected(mockTunnelEndpoint, null), + tunnelState = + TunnelState.Connected(mockTunnelEndpoint, null, emptyList()), inAddress = mockInAddress, outAddress = mockOutAddress, showLocation = false, @@ -608,7 +618,7 @@ class ConnectScreenTest { ConnectUiState( location = null, selectedRelayItemTitle = null, - tunnelState = TunnelState.Connecting(null, null), + tunnelState = TunnelState.Connecting(null, null, emptyList()), inAddress = null, outAddress = "", showLocation = false, @@ -640,7 +650,7 @@ class ConnectScreenTest { ConnectUiState( location = null, selectedRelayItemTitle = null, - tunnelState = TunnelState.Connecting(null, null), + tunnelState = TunnelState.Connecting(null, null, emptyList()), inAddress = null, outAddress = "", showLocation = false, @@ -671,7 +681,7 @@ class ConnectScreenTest { ConnectUiState( location = null, selectedRelayItemTitle = null, - tunnelState = TunnelState.Connecting(null, null), + tunnelState = TunnelState.Connecting(null, null, emptyList()), inAddress = null, outAddress = "", showLocation = false, @@ -704,7 +714,7 @@ class ConnectScreenTest { ConnectUiState( location = null, selectedRelayItemTitle = null, - tunnelState = TunnelState.Connecting(null, null), + tunnelState = TunnelState.Connecting(null, null, emptyList()), inAddress = null, outAddress = "", showLocation = false, diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/OutOfTimeScreenTest.kt b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/OutOfTimeScreenTest.kt index 7dc378261d2f..3ead3828d304 100644 --- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/OutOfTimeScreenTest.kt +++ b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/OutOfTimeScreenTest.kt @@ -129,7 +129,7 @@ class OutOfTimeScreenTest { OutOfTimeScreen( state = OutOfTimeUiState( - tunnelState = TunnelState.Connecting(null, null), + tunnelState = TunnelState.Connecting(null, null, emptyList()), deviceName = "", showSitePayment = true ), diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/button/ConnectionButton.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/button/ConnectionButton.kt index eda1b866903e..dd8eec57d23f 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/button/ConnectionButton.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/button/ConnectionButton.kt @@ -29,15 +29,33 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import androidx.constraintlayout.compose.ConstraintLayout import androidx.constraintlayout.compose.Dimension import net.mullvad.mullvadvpn.R +import net.mullvad.mullvadvpn.compose.preview.TunnelStatePreviewParameterProvider import net.mullvad.mullvadvpn.lib.model.TunnelState import net.mullvad.mullvadvpn.lib.theme.AppTheme import net.mullvad.mullvadvpn.lib.theme.Dimens import net.mullvad.mullvadvpn.lib.theme.color.AlphaDisconnectButton +@Composable +@Preview +private fun PreviewConnectionButton( + @PreviewParameter(TunnelStatePreviewParameterProvider::class) tunnelState: TunnelState +) { + AppTheme { + ConnectionButton( + state = tunnelState, + disconnectClick = {}, + reconnectClick = {}, + cancelClick = {}, + connectClick = {} + ) + } +} + @Composable fun ConnectionButton( modifier: Modifier = Modifier, @@ -106,21 +124,6 @@ fun ConnectionButton( ) } -@Preview -@Composable -private fun PreviewConnectionButton() { - AppTheme { - ConnectionButton( - text = "Disconnect", - mainClick = {}, - containerColor = MaterialTheme.colorScheme.error.copy(alpha = AlphaDisconnectButton), - contentColor = MaterialTheme.colorScheme.onError, - reconnectClick = {}, - isReconnectButtonEnabled = true - ) - } -} - @OptIn(ExperimentalMaterial3Api::class) @Composable private fun ConnectionButton( diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/ConnectionStatusText.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/ConnectionStatusText.kt index b4d6b0d60ca7..0b155953e337 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/ConnectionStatusText.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/ConnectionStatusText.kt @@ -1,66 +1,29 @@ package net.mullvad.mullvadvpn.compose.component import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Column import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview -import java.net.InetSocketAddress +import androidx.compose.ui.tooling.preview.PreviewParameter import net.mullvad.mullvadvpn.R +import net.mullvad.mullvadvpn.compose.preview.TunnelStatePreviewParameterProvider import net.mullvad.mullvadvpn.lib.model.ActionAfterDisconnect -import net.mullvad.mullvadvpn.lib.model.Endpoint -import net.mullvad.mullvadvpn.lib.model.ErrorState -import net.mullvad.mullvadvpn.lib.model.ErrorStateCause -import net.mullvad.mullvadvpn.lib.model.TransportProtocol -import net.mullvad.mullvadvpn.lib.model.TunnelEndpoint import net.mullvad.mullvadvpn.lib.model.TunnelState import net.mullvad.mullvadvpn.lib.theme.AppTheme import net.mullvad.mullvadvpn.lib.theme.typeface.connectionStatus @Preview @Composable -private fun PreviewConnectionStatusText() { +private fun PreviewConnectionStatusText( + @PreviewParameter(TunnelStatePreviewParameterProvider::class) tunnelState: TunnelState +) { AppTheme { - SpacedColumn(modifier = Modifier.background(MaterialTheme.colorScheme.surface)) { - ConnectionStatusText(TunnelState.Disconnected()) - ConnectionStatusText(TunnelState.Connecting(null, null)) - ConnectionStatusText( - state = TunnelState.Error(ErrorState(ErrorStateCause.Ipv6Unavailable, true)) - ) - ConnectionStatusText( - state = - TunnelState.Connected( - endpoint = - TunnelEndpoint( - endpoint = - Endpoint( - address = InetSocketAddress(10), - protocol = TransportProtocol.Tcp - ), - quantumResistant = false, - obfuscation = null - ), - location = null - ) - ) - ConnectionStatusText( - state = - TunnelState.Connected( - endpoint = - TunnelEndpoint( - endpoint = - Endpoint( - address = InetSocketAddress(10), - protocol = TransportProtocol.Tcp - ), - quantumResistant = true, - obfuscation = null - ), - location = null - ) - ) + Column(modifier = Modifier.background(MaterialTheme.colorScheme.surface)) { + ConnectionStatusText(state = tunnelState) } } } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/preview/OutOfTimeScreenPreviewParameterProvider.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/preview/OutOfTimeScreenPreviewParameterProvider.kt new file mode 100644 index 000000000000..efe8ac2fa53b --- /dev/null +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/preview/OutOfTimeScreenPreviewParameterProvider.kt @@ -0,0 +1,29 @@ +package net.mullvad.mullvadvpn.compose.preview + +import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import net.mullvad.mullvadvpn.compose.preview.TunnelStatePreviewData.generateConnectingState +import net.mullvad.mullvadvpn.compose.preview.TunnelStatePreviewData.generateDisconnectedState +import net.mullvad.mullvadvpn.compose.preview.TunnelStatePreviewData.generateErrorState +import net.mullvad.mullvadvpn.compose.state.OutOfTimeUiState + +class OutOfTimeScreenPreviewParameterProvider : PreviewParameterProvider { + override val values: Sequence = + sequenceOf( + OutOfTimeUiState( + tunnelState = generateDisconnectedState(), + "Heroic Frog", + showSitePayment = true + ), + OutOfTimeUiState( + tunnelState = + generateConnectingState(featureIndicators = 0, quantumResistant = false), + "Strong Rabbit", + showSitePayment = true + ), + OutOfTimeUiState( + tunnelState = generateErrorState(isBlocking = true), + deviceName = "Stable Horse", + showSitePayment = true + ) + ) +} diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/preview/TunnelStatePreviewData.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/preview/TunnelStatePreviewData.kt new file mode 100644 index 000000000000..7045cc45dcf6 --- /dev/null +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/preview/TunnelStatePreviewData.kt @@ -0,0 +1,70 @@ +package net.mullvad.mullvadvpn.compose.preview + +import java.net.InetSocketAddress +import net.mullvad.mullvadvpn.lib.model.ActionAfterDisconnect +import net.mullvad.mullvadvpn.lib.model.Endpoint +import net.mullvad.mullvadvpn.lib.model.ErrorState +import net.mullvad.mullvadvpn.lib.model.ErrorStateCause +import net.mullvad.mullvadvpn.lib.model.FeatureIndicator +import net.mullvad.mullvadvpn.lib.model.GeoIpLocation +import net.mullvad.mullvadvpn.lib.model.ObfuscationEndpoint +import net.mullvad.mullvadvpn.lib.model.ObfuscationType +import net.mullvad.mullvadvpn.lib.model.TransportProtocol +import net.mullvad.mullvadvpn.lib.model.TunnelEndpoint +import net.mullvad.mullvadvpn.lib.model.TunnelState + +object TunnelStatePreviewData { + fun generateDisconnectedState() = TunnelState.Disconnected() + + fun generateConnectingState(featureIndicators: Int, quantumResistant: Boolean) = + TunnelState.Connecting( + endpoint = generateTunnelEndpoint(quantumResistant = quantumResistant), + location = generateLocation(), + featureIndicators = generateFeatureIndicators(featureIndicators) + ) + + fun generateConnectedState(featureIndicators: Int, quantumResistant: Boolean) = + TunnelState.Connected( + endpoint = generateTunnelEndpoint(quantumResistant = quantumResistant), + location = generateLocation(), + featureIndicators = generateFeatureIndicators(featureIndicators) + ) + + fun generateDisconnectingState(actionAfterDisconnect: ActionAfterDisconnect) = + TunnelState.Disconnecting(actionAfterDisconnect = actionAfterDisconnect) + + fun generateErrorState(isBlocking: Boolean) = + TunnelState.Error( + errorState = ErrorState(cause = ErrorStateCause.DnsError, isBlocking = isBlocking) + ) +} + +private fun generateTunnelEndpoint(quantumResistant: Boolean): TunnelEndpoint = + TunnelEndpoint( + endpoint = generateEndpoint(TransportProtocol.Udp), + quantumResistant = quantumResistant, + obfuscation = + ObfuscationEndpoint( + endpoint = generateEndpoint(TransportProtocol.Tcp), + ObfuscationType.Udp2Tcp + ) + ) + +private fun generateEndpoint(transportProtocol: TransportProtocol) = + Endpoint(address = InetSocketAddress(DEFAULT_ENDPOINT_PORT), protocol = transportProtocol) + +private fun generateLocation(): GeoIpLocation = + GeoIpLocation( + ipv4 = null, + ipv6 = null, + country = "", + city = "", + hostname = "", + latitude = 0.0, + longitude = 0.0, + ) + +private fun generateFeatureIndicators(size: Int): List = + FeatureIndicator.entries.subList(0, size) + +private const val DEFAULT_ENDPOINT_PORT = 100 diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/preview/TunnelStatePreviewParameterProvider.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/preview/TunnelStatePreviewParameterProvider.kt new file mode 100644 index 000000000000..9cd06ef276fc --- /dev/null +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/preview/TunnelStatePreviewParameterProvider.kt @@ -0,0 +1,26 @@ +package net.mullvad.mullvadvpn.compose.preview + +import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import net.mullvad.mullvadvpn.compose.preview.TunnelStatePreviewData.generateConnectedState +import net.mullvad.mullvadvpn.compose.preview.TunnelStatePreviewData.generateConnectingState +import net.mullvad.mullvadvpn.compose.preview.TunnelStatePreviewData.generateDisconnectedState +import net.mullvad.mullvadvpn.compose.preview.TunnelStatePreviewData.generateDisconnectingState +import net.mullvad.mullvadvpn.compose.preview.TunnelStatePreviewData.generateErrorState +import net.mullvad.mullvadvpn.lib.model.ActionAfterDisconnect +import net.mullvad.mullvadvpn.lib.model.TunnelState + +class TunnelStatePreviewParameterProvider : PreviewParameterProvider { + override val values: Sequence = + sequenceOf( + generateDisconnectedState(), + generateConnectingState(featureIndicators = 0, quantumResistant = false), + generateConnectingState(featureIndicators = 0, quantumResistant = true), + generateConnectedState(featureIndicators = 0, quantumResistant = false), + generateConnectedState(featureIndicators = 0, quantumResistant = true), + generateDisconnectingState(actionAfterDisconnect = ActionAfterDisconnect.Block), + generateDisconnectingState(actionAfterDisconnect = ActionAfterDisconnect.Nothing), + generateDisconnectingState(actionAfterDisconnect = ActionAfterDisconnect.Reconnect), + generateErrorState(isBlocking = true), + generateErrorState(isBlocking = false) + ) +} diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/OutOfTimeScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/OutOfTimeScreen.kt index 7b5b4bff5542..43730ff164aa 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/OutOfTimeScreen.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/OutOfTimeScreen.kt @@ -24,6 +24,7 @@ import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.lifecycle.Lifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.dropUnlessResumed @@ -48,12 +49,12 @@ import net.mullvad.mullvadvpn.compose.component.ScaffoldWithTopBarAndDeviceName import net.mullvad.mullvadvpn.compose.component.drawVerticalScrollbar import net.mullvad.mullvadvpn.compose.extensions.createOpenAccountPageHook import net.mullvad.mullvadvpn.compose.extensions.dropUnlessResumed +import net.mullvad.mullvadvpn.compose.preview.OutOfTimeScreenPreviewParameterProvider import net.mullvad.mullvadvpn.compose.state.OutOfTimeUiState import net.mullvad.mullvadvpn.compose.test.OUT_OF_TIME_SCREEN_TITLE_TEST_TAG import net.mullvad.mullvadvpn.compose.transitions.HomeTransition import net.mullvad.mullvadvpn.compose.util.CollectSideEffectWithLifecycle import net.mullvad.mullvadvpn.compose.util.showSnackbarImmediately -import net.mullvad.mullvadvpn.lib.model.ErrorState import net.mullvad.mullvadvpn.lib.model.ErrorStateCause import net.mullvad.mullvadvpn.lib.model.TunnelState import net.mullvad.mullvadvpn.lib.payment.model.ProductId @@ -65,48 +66,12 @@ import org.koin.androidx.compose.koinViewModel @Preview @Composable -private fun PreviewOutOfTimeScreenDisconnected() { - AppTheme { - OutOfTimeScreen( - state = - OutOfTimeUiState( - tunnelState = TunnelState.Disconnected(), - "Heroic Frog", - showSitePayment = true - ), - ) - } -} - -@Preview -@Composable -private fun PreviewOutOfTimeScreenConnecting() { - AppTheme { - OutOfTimeScreen( - state = - OutOfTimeUiState( - tunnelState = TunnelState.Connecting(null, null), - "Strong Rabbit", - showSitePayment = true - ), - ) - } -} - -@Preview -@Composable -private fun PreviewOutOfTimeScreenError() { +private fun PreviewOutOfTimeScreen( + @PreviewParameter(OutOfTimeScreenPreviewParameterProvider::class) state: OutOfTimeUiState +) { AppTheme { OutOfTimeScreen( - state = - OutOfTimeUiState( - tunnelState = - TunnelState.Error( - ErrorState(cause = ErrorStateCause.IsOffline, isBlocking = true) - ), - deviceName = "Stable Horse", - showSitePayment = true - ), + state = state, ) } } diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/usecase/OutOfTimeUseCaseTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/usecase/OutOfTimeUseCaseTest.kt index 088c9a435c85..ef7944a5a877 100644 --- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/usecase/OutOfTimeUseCaseTest.kt +++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/usecase/OutOfTimeUseCaseTest.kt @@ -90,8 +90,8 @@ class OutOfTimeUseCaseTest { val tunnelStateChanges = listOf( TunnelState.Disconnected(), - TunnelState.Connected(mockk(), null), - TunnelState.Connecting(null, null), + TunnelState.Connected(mockk(), null, emptyList()), + TunnelState.Connecting(null, null, emptyList()), TunnelState.Disconnecting(mockk()), TunnelState.Error(ErrorState(ErrorStateCause.StartTunnelError, false)), ) diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/ConnectViewModelTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/ConnectViewModelTest.kt index 50a16d1432d0..855eea238d6a 100644 --- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/ConnectViewModelTest.kt +++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/ConnectViewModelTest.kt @@ -147,7 +147,8 @@ class ConnectViewModelTest { @Test fun `given change in tunnelRealState uiState should emit new tunnelRealState`() = runTest { - val tunnelRealStateTestItem = TunnelState.Connected(mockk(relaxed = true), null) + val tunnelRealStateTestItem = + TunnelState.Connected(mockk(relaxed = true), null, emptyList()) viewModel.uiState.test { assertEquals(ConnectUiState.INITIAL, awaitItem()) @@ -162,7 +163,7 @@ class ConnectViewModelTest { // Arrange val tunnelEndpoint: TunnelEndpoint = mockk() val location: GeoIpLocation = mockk() - val tunnelStateTestItem = TunnelState.Connected(tunnelEndpoint, location) + val tunnelStateTestItem = TunnelState.Connected(tunnelEndpoint, location, emptyList()) every { tunnelEndpoint.toInAddress() } returns mockk(relaxed = true) every { location.toOutAddress() } returns "1.1.1.1" every { location.hostname } returns "hostname" diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/OutOfTimeViewModelTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/OutOfTimeViewModelTest.kt index 886cb58fdaf0..047369b5cb46 100644 --- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/OutOfTimeViewModelTest.kt +++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/OutOfTimeViewModelTest.kt @@ -117,7 +117,7 @@ class OutOfTimeViewModelTest { @Test fun `when tunnel state changes then ui should be updated`() = runTest { // Arrange - val tunnelRealStateTestItem = TunnelState.Connected(mockk(), mockk()) + val tunnelRealStateTestItem = TunnelState.Connected(mockk(), mockk(), emptyList()) // Act, Assert viewModel.uiState.test { diff --git a/android/lib/daemon-grpc/src/main/kotlin/net/mullvad/mullvadvpn/lib/daemon/grpc/mapper/ToDomain.kt b/android/lib/daemon-grpc/src/main/kotlin/net/mullvad/mullvadvpn/lib/daemon/grpc/mapper/ToDomain.kt index 9baa42669641..ca4e924b6cdf 100644 --- a/android/lib/daemon-grpc/src/main/kotlin/net/mullvad/mullvadvpn/lib/daemon/grpc/mapper/ToDomain.kt +++ b/android/lib/daemon-grpc/src/main/kotlin/net/mullvad/mullvadvpn/lib/daemon/grpc/mapper/ToDomain.kt @@ -34,6 +34,7 @@ import net.mullvad.mullvadvpn.lib.model.DnsState import net.mullvad.mullvadvpn.lib.model.Endpoint import net.mullvad.mullvadvpn.lib.model.ErrorState import net.mullvad.mullvadvpn.lib.model.ErrorStateCause +import net.mullvad.mullvadvpn.lib.model.FeatureIndicator import net.mullvad.mullvadvpn.lib.model.GeoIpLocation import net.mullvad.mullvadvpn.lib.model.GeoLocationId import net.mullvad.mullvadvpn.lib.model.Mtu @@ -89,7 +90,8 @@ internal fun ManagementInterface.TunnelState.toDomain(): TunnelState = if (hasLocation()) { location.toDomain() } else null - } + }, + featureIndicators = connected.featureIndicators.toDomain() ) ManagementInterface.TunnelState.StateCase.CONNECTED -> TunnelState.Connected( @@ -101,7 +103,8 @@ internal fun ManagementInterface.TunnelState.toDomain(): TunnelState = } else { null } - } + }, + featureIndicators = connected.featureIndicators.toDomain() ) ManagementInterface.TunnelState.StateCase.DISCONNECTING -> TunnelState.Disconnecting( @@ -578,3 +581,28 @@ internal fun ManagementInterface.Socks5Remote.toDomain(): ApiAccessMethod.Custom internal fun ManagementInterface.SocksAuth.toDomain(): SocksAuth = SocksAuth(username = username, password = password) + +internal fun ManagementInterface.FeatureIndicators.toDomain(): List = + this.activeFeaturesList.map { it.toDomain() } + +internal fun ManagementInterface.FeatureIndicator.toDomain() = + when (this) { + ManagementInterface.FeatureIndicator.QUANTUM_RESISTANCE -> + FeatureIndicator.QUANTUM_RESISTANCE + ManagementInterface.FeatureIndicator.SPLIT_TUNNELING -> FeatureIndicator.SPLIT_TUNNELING + ManagementInterface.FeatureIndicator.UDP_2_TCP -> FeatureIndicator.UDP_2_TCP + ManagementInterface.FeatureIndicator.LAN_SHARING -> FeatureIndicator.LAN_SHARING + ManagementInterface.FeatureIndicator.DNS_CONTENT_BLOCKERS -> + FeatureIndicator.DNS_CONTENT_BLOCKERS + ManagementInterface.FeatureIndicator.CUSTOM_DNS -> FeatureIndicator.CUSTOM_DNS + ManagementInterface.FeatureIndicator.SERVER_IP_OVERRIDE -> + FeatureIndicator.SERVER_IP_OVERRIDE + ManagementInterface.FeatureIndicator.CUSTOM_MTU -> FeatureIndicator.CUSTOM_MTU + ManagementInterface.FeatureIndicator.LOCKDOWN_MODE, + ManagementInterface.FeatureIndicator.SHADOWSOCKS, + ManagementInterface.FeatureIndicator.MULTIHOP, + ManagementInterface.FeatureIndicator.BRIDGE_MODE, + ManagementInterface.FeatureIndicator.CUSTOM_MSS_FIX, + ManagementInterface.FeatureIndicator.DAITA, + ManagementInterface.FeatureIndicator.UNRECOGNIZED -> error("Feature not supported") + } diff --git a/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/FeatureIndicator.kt b/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/FeatureIndicator.kt new file mode 100644 index 000000000000..7ad0b3ab6998 --- /dev/null +++ b/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/FeatureIndicator.kt @@ -0,0 +1,20 @@ +package net.mullvad.mullvadvpn.lib.model + +enum class FeatureIndicator { + QUANTUM_RESISTANCE, + SPLIT_TUNNELING, + UDP_2_TCP, + LAN_SHARING, + DNS_CONTENT_BLOCKERS, + CUSTOM_DNS, + SERVER_IP_OVERRIDE, + CUSTOM_MTU, + // Currently not supported + // LOCKDOWN_MODE, + // SHADOWSOCKS, + // MULTIHOP, + // BRIDGE_MODE, + // CUSTOM_MSS_FIX, + // DAITA, + // UNRECOGNIZED, +} diff --git a/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/TunnelState.kt b/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/TunnelState.kt index 3fae41802a2c..8ed43bd294b2 100644 --- a/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/TunnelState.kt +++ b/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/TunnelState.kt @@ -3,11 +3,17 @@ package net.mullvad.mullvadvpn.lib.model sealed class TunnelState { data class Disconnected(val location: GeoIpLocation? = null) : TunnelState() - data class Connecting(val endpoint: TunnelEndpoint?, val location: GeoIpLocation?) : - TunnelState() + data class Connecting( + val endpoint: TunnelEndpoint?, + val location: GeoIpLocation?, + val featureIndicators: List + ) : TunnelState() - data class Connected(val endpoint: TunnelEndpoint, val location: GeoIpLocation?) : - TunnelState() + data class Connected( + val endpoint: TunnelEndpoint, + val location: GeoIpLocation?, + val featureIndicators: List + ) : TunnelState() data class Disconnecting(val actionAfterDisconnect: ActionAfterDisconnect) : TunnelState()