summaryrefslogtreecommitdiffhomepage
path: root/android
diff options
context:
space:
mode:
authorJonatan Rhodin <jonatan.rhodin@mullvad.net>2024-08-26 10:35:29 +0200
committerAlbin <albin@mullvad.net>2024-08-26 12:44:32 +0200
commit4cefa1584d102d47110606b9797f78801103f35f (patch)
treeea679771695dc63c0d6b4a6906d90998c2eaabcd /android
parentec2098841f78621b31708ab6427c740f7e42ce5a (diff)
downloadmullvadvpn-4cefa1584d102d47110606b9797f78801103f35f.tar.xz
mullvadvpn-4cefa1584d102d47110606b9797f78801103f35f.zip
Add support for feature indicators for tunnel state
Diffstat (limited to 'android')
-rw-r--r--android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/ConnectScreenTest.kt34
-rw-r--r--android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/OutOfTimeScreenTest.kt2
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/button/ConnectionButton.kt33
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/ConnectionStatusText.kt53
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/preview/OutOfTimeScreenPreviewParameterProvider.kt29
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/preview/TunnelStatePreviewData.kt70
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/preview/TunnelStatePreviewParameterProvider.kt26
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/OutOfTimeScreen.kt47
-rw-r--r--android/app/src/test/kotlin/net/mullvad/mullvadvpn/usecase/OutOfTimeUseCaseTest.kt4
-rw-r--r--android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/ConnectViewModelTest.kt5
-rw-r--r--android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/OutOfTimeViewModelTest.kt2
-rw-r--r--android/lib/daemon-grpc/src/main/kotlin/net/mullvad/mullvadvpn/lib/daemon/grpc/mapper/ToDomain.kt32
-rw-r--r--android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/FeatureIndicator.kt20
-rw-r--r--android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/TunnelState.kt14
14 files changed, 246 insertions, 125 deletions
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 9ba5db81bc..435bbeab00 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 7dc378261d..3ead3828d3 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 eda1b86690..dd8eec57d2 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,16 +29,34 @@ 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,
reconnectButtonTestTag: String = "",
@@ -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 b4d6b0d60c..0b155953e3 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 0000000000..efe8ac2fa5
--- /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<OutOfTimeUiState> {
+ override val values: Sequence<OutOfTimeUiState> =
+ 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 0000000000..7045cc45dc
--- /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> =
+ 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 0000000000..9cd06ef276
--- /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<TunnelState> {
+ override val values: Sequence<TunnelState> =
+ 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 7b5b4bff55..43730ff164 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 088c9a435c..ef7944a5a8 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 50a16d1432..855eea238d 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 886cb58fda..047369b5cb 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 9baa426696..ca4e924b6c 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<FeatureIndicator> =
+ 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 0000000000..7ad0b3ab69
--- /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 3fae41802a..8ed43bd294 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<FeatureIndicator>
+ ) : TunnelState()
- data class Connected(val endpoint: TunnelEndpoint, val location: GeoIpLocation?) :
- TunnelState()
+ data class Connected(
+ val endpoint: TunnelEndpoint,
+ val location: GeoIpLocation?,
+ val featureIndicators: List<FeatureIndicator>
+ ) : TunnelState()
data class Disconnecting(val actionAfterDisconnect: ActionAfterDisconnect) : TunnelState()