Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for feature indicators for tunnel state #6675

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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(
Expand Down
Original file line number Diff line number Diff line change
@@ -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)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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
)
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
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(100), 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)
Original file line number Diff line number Diff line change
@@ -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)
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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,
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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(
Expand All @@ -101,7 +103,8 @@ internal fun ManagementInterface.TunnelState.toDomain(): TunnelState =
} else {
null
}
}
},
featureIndicators = connected.featureIndicators.toDomain()
)
ManagementInterface.TunnelState.StateCase.DISCONNECTING ->
TunnelState.Disconnecting(
Expand Down Expand Up @@ -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")
}
Loading
Loading