diff options
| author | Jonatan Rhodin <jonatan.rhodin@mullvad.net> | 2023-04-27 07:59:07 +0200 |
|---|---|---|
| committer | Jonatan Rhodin <jonatan.rhodin@mullvad.net> | 2023-05-09 16:18:13 +0200 |
| commit | 9638d005d4da49eab3f94c3c3ae6b4d9637f9a3b (patch) | |
| tree | c28273fa29f45a83e4086ae7b5ca4e994aab87e8 /android/app/src | |
| parent | 45a4e25733e305eed244a3ebd1a1e88b3d5cb361 (diff) | |
| download | mullvadvpn-9638d005d4da49eab3f94c3c3ae6b4d9637f9a3b.tar.xz mullvadvpn-9638d005d4da49eab3f94c3c3ae6b4d9637f9a3b.zip | |
Add compose ui to udp2tcp feature
- New setting in advanced settings for obfuscation
- Add info dialog about obfuscation
- Send obfuscation setting to daemon
- Reflect udp2tcp state in the ui
Diffstat (limited to 'android/app/src')
17 files changed, 276 insertions, 26 deletions
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/BaseCell.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/BaseCell.kt index bab992bcc1..56a5a2b38b 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/BaseCell.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/BaseCell.kt @@ -23,9 +23,9 @@ import net.mullvad.mullvadvpn.compose.theme.MullvadDarkBlue @Composable fun BaseCell( - title: @Composable () -> Unit, - bodyView: @Composable () -> Unit, modifier: Modifier = Modifier, + title: @Composable () -> Unit, + bodyView: @Composable () -> Unit = {}, isRowEnabled: Boolean = true, onCellClicked: () -> Unit = {}, subtitle: @Composable (() -> Unit)? = null, diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/InformationComposeCell.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/InformationComposeCell.kt new file mode 100644 index 0000000000..f276defee8 --- /dev/null +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/InformationComposeCell.kt @@ -0,0 +1,81 @@ +package net.mullvad.mullvadvpn.compose.cell + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.wrapContentHeight +import androidx.compose.foundation.layout.wrapContentWidth +import androidx.compose.material.Icon +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.alpha +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.dimensionResource +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import net.mullvad.mullvadvpn.R +import net.mullvad.mullvadvpn.compose.theme.AlphaActive +import net.mullvad.mullvadvpn.compose.theme.AlphaInactive +import net.mullvad.mullvadvpn.compose.theme.MullvadBlue +import net.mullvad.mullvadvpn.compose.theme.MullvadWhite + +@Preview +@Composable +private fun PreviewInformationComposeCell() { + InformationComposeCell( + title = "Information row title", + isEnabled = true, + onCellClicked = {}, + onInfoClicked = {} + ) +} + +@Composable +fun InformationComposeCell( + title: String, + isEnabled: Boolean = true, + background: Color = MullvadBlue, + onCellClicked: () -> Unit = {}, + onInfoClicked: (() -> Unit)? = null +) { + val titleModifier = Modifier.alpha(if (isEnabled) AlphaActive else AlphaInactive) + val bodyViewModifier = Modifier + + BaseCell( + title = { SwitchCellTitle(title = title, modifier = titleModifier) }, + background = background, + bodyView = { + InformationComposeCellBody(modifier = bodyViewModifier, onInfoClicked = onInfoClicked) + }, + onCellClicked = onCellClicked + ) +} + +@Composable +private fun InformationComposeCellBody(modifier: Modifier, onInfoClicked: (() -> Unit)? = null) { + val horizontalPadding = dimensionResource(id = R.dimen.medium_padding) + val verticalPadding = 13.dp + Row( + modifier = modifier.wrapContentWidth().wrapContentHeight(), + verticalAlignment = Alignment.CenterVertically + ) { + if (onInfoClicked != null) { + Icon( + modifier = + Modifier.clickable { onInfoClicked() } + .padding( + start = horizontalPadding, + end = horizontalPadding, + top = verticalPadding, + bottom = verticalPadding + ) + .align(Alignment.CenterVertically), + painter = painterResource(id = R.drawable.icon_info), + contentDescription = null, + tint = MullvadWhite + ) + } + } +} diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/SwitchComposeCell.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/SwitchComposeCell.kt index 79d5209a7f..980af59693 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/SwitchComposeCell.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/SwitchComposeCell.kt @@ -76,7 +76,7 @@ fun SwitchComposeCell( } @Composable -fun SwitchCellTitle(title: String, modifier: Modifier) { +fun SwitchCellTitle(title: String, modifier: Modifier = Modifier) { val textSize = dimensionResource(id = R.dimen.text_medium_plus).value.sp Text( text = title, diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/ObfuscationInfoDialog.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/ObfuscationInfoDialog.kt new file mode 100644 index 0000000000..323476a034 --- /dev/null +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/ObfuscationInfoDialog.kt @@ -0,0 +1,10 @@ +package net.mullvad.mullvadvpn.compose.dialog + +import androidx.compose.runtime.Composable +import androidx.compose.ui.res.stringResource +import net.mullvad.mullvadvpn.R + +@Composable +fun ObfuscationInfoDialog(onDismiss: () -> Unit) { + InfoDialog(message = stringResource(id = R.string.obfuscation_info), onDismiss = onDismiss) +} diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/UdpOverTcpPortInfoDialog.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/UdpOverTcpPortInfoDialog.kt new file mode 100644 index 0000000000..223791838a --- /dev/null +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/UdpOverTcpPortInfoDialog.kt @@ -0,0 +1,13 @@ +package net.mullvad.mullvadvpn.compose.dialog + +import androidx.compose.runtime.Composable +import androidx.compose.ui.res.stringResource +import net.mullvad.mullvadvpn.R + +@Composable +fun UdpOverTcpPortInfoDialog(onDismiss: () -> Unit) { + InfoDialog( + message = stringResource(id = R.string.udp_over_tcp_port_info), + onDismiss = onDismiss + ) +} diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/AdvancedSettingScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/AdvancedSettingScreen.kt index 3bb4fcd6db..c07d188e42 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/AdvancedSettingScreen.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/AdvancedSettingScreen.kt @@ -47,8 +47,10 @@ import net.mullvad.mullvadvpn.compose.cell.ContentBlockersDisableModeCellSubtitl import net.mullvad.mullvadvpn.compose.cell.CustomDnsCellSubtitle import net.mullvad.mullvadvpn.compose.cell.DnsCell import net.mullvad.mullvadvpn.compose.cell.ExpandableComposeCell +import net.mullvad.mullvadvpn.compose.cell.InformationComposeCell import net.mullvad.mullvadvpn.compose.cell.MtuComposeCell import net.mullvad.mullvadvpn.compose.cell.NavigationComposeCell +import net.mullvad.mullvadvpn.compose.cell.SwitchCellTitle import net.mullvad.mullvadvpn.compose.cell.SwitchComposeCell import net.mullvad.mullvadvpn.compose.component.CollapsableAwareToolbarScaffold import net.mullvad.mullvadvpn.compose.component.CollapsingTopBar @@ -58,10 +60,13 @@ import net.mullvad.mullvadvpn.compose.dialog.CustomDnsInfoDialog import net.mullvad.mullvadvpn.compose.dialog.DnsDialog import net.mullvad.mullvadvpn.compose.dialog.MalwareInfoDialog import net.mullvad.mullvadvpn.compose.dialog.MtuDialog +import net.mullvad.mullvadvpn.compose.dialog.ObfuscationInfoDialog import net.mullvad.mullvadvpn.compose.extensions.itemWithDivider import net.mullvad.mullvadvpn.compose.state.AdvancedSettingsUiState import net.mullvad.mullvadvpn.compose.theme.MullvadBlue20 import net.mullvad.mullvadvpn.compose.theme.MullvadDarkBlue +import net.mullvad.mullvadvpn.compose.theme.MullvadGreen +import net.mullvad.mullvadvpn.model.SelectedObfuscation import net.mullvad.mullvadvpn.viewmodel.CustomDnsItem @OptIn(ExperimentalMaterialApi::class) @@ -97,7 +102,10 @@ private fun PreviewAdvancedSettings() { onCustomDnsInfoClicked = {}, onDismissInfoClicked = {}, onBackClick = {}, - toastMessagesSharedFlow = MutableSharedFlow<String>().asSharedFlow() + toastMessagesSharedFlow = MutableSharedFlow<String>().asSharedFlow(), + onStopEvent = {}, + onSelectObfuscationSetting = {}, + onObfuscationInfoClicked = {} ) } @@ -130,7 +138,9 @@ fun AdvancedSettingScreen( onDismissInfoClicked: () -> Unit = {}, onBackClick: () -> Unit = {}, onStopEvent: () -> Unit = {}, - toastMessagesSharedFlow: SharedFlow<String> + toastMessagesSharedFlow: SharedFlow<String>, + onSelectObfuscationSetting: (selectedObfuscation: SelectedObfuscation) -> Unit = {}, + onObfuscationInfoClicked: () -> Unit = {} ) { val cellVerticalSpacing = dimensionResource(id = R.dimen.cell_label_vertical_padding) val cellHorizontalSpacing = dimensionResource(id = R.dimen.cell_left_padding) @@ -164,6 +174,9 @@ fun AdvancedSettingScreen( is AdvancedSettingsUiState.MalwareInfoDialogUiState -> { MalwareInfoDialog(onDismissInfoClicked) } + is AdvancedSettingsUiState.ObfuscationInfoDialogUiState -> { + ObfuscationInfoDialog(onDismissInfoClicked) + } else -> { // NOOP } @@ -303,6 +316,62 @@ fun AdvancedSettingScreen( } } + itemWithDivider { + Spacer(modifier = Modifier.height(cellVerticalSpacing)) + InformationComposeCell( + title = stringResource(R.string.obfuscation_title), + onInfoClicked = { onObfuscationInfoClicked() } + ) + } + itemWithDivider { + BaseCell( + onCellClicked = { onSelectObfuscationSetting(SelectedObfuscation.Auto) }, + title = { + SwitchCellTitle( + title = stringResource(id = R.string.automatic), + ) + }, + background = + if (uiState.selectedObfuscation == SelectedObfuscation.Auto) { + MullvadGreen + } else { + MullvadBlue20 + } + ) + } + itemWithDivider { + BaseCell( + onCellClicked = { onSelectObfuscationSetting(SelectedObfuscation.Udp2Tcp) }, + title = { + SwitchCellTitle( + title = stringResource(id = R.string.obfuscation_on_udp_over_tcp), + ) + }, + background = + if (uiState.selectedObfuscation == SelectedObfuscation.Udp2Tcp) { + MullvadGreen + } else { + MullvadBlue20 + } + ) + } + itemWithDivider { + BaseCell( + onCellClicked = { onSelectObfuscationSetting(SelectedObfuscation.Off) }, + title = { + SwitchCellTitle( + title = stringResource(id = R.string.off), + ) + }, + background = + if (uiState.selectedObfuscation == SelectedObfuscation.Off) { + MullvadGreen + } else { + MullvadBlue20 + } + ) + } + item { Spacer(modifier = Modifier.height(cellVerticalSpacing)) SwitchComposeCell( diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/AdvancedSettingsUiState.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/AdvancedSettingsUiState.kt index feaa8e0824..f08a654078 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/AdvancedSettingsUiState.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/AdvancedSettingsUiState.kt @@ -1,6 +1,7 @@ package net.mullvad.mullvadvpn.compose.state import net.mullvad.mullvadvpn.model.DefaultDnsOptions +import net.mullvad.mullvadvpn.model.SelectedObfuscation import net.mullvad.mullvadvpn.viewmodel.CustomDnsItem import net.mullvad.mullvadvpn.viewmodel.StagedDns @@ -10,13 +11,15 @@ sealed interface AdvancedSettingsUiState { val customDnsItems: List<CustomDnsItem> val contentBlockersOptions: DefaultDnsOptions val isAllowLanEnabled: Boolean + val selectedObfuscation: SelectedObfuscation data class DefaultUiState( override val mtu: String = "", override val isCustomDnsEnabled: Boolean = false, override val isAllowLanEnabled: Boolean = false, override val customDnsItems: List<CustomDnsItem> = listOf(), - override val contentBlockersOptions: DefaultDnsOptions = DefaultDnsOptions() + override val contentBlockersOptions: DefaultDnsOptions = DefaultDnsOptions(), + override val selectedObfuscation: SelectedObfuscation = SelectedObfuscation.Off, ) : AdvancedSettingsUiState data class MtuDialogUiState( @@ -25,7 +28,8 @@ sealed interface AdvancedSettingsUiState { override val isAllowLanEnabled: Boolean = false, override val customDnsItems: List<CustomDnsItem> = listOf(), override val contentBlockersOptions: DefaultDnsOptions = DefaultDnsOptions(), - val mtuEditValue: String + val mtuEditValue: String, + override val selectedObfuscation: SelectedObfuscation = SelectedObfuscation.Off, ) : AdvancedSettingsUiState data class DnsDialogUiState( @@ -34,7 +38,8 @@ sealed interface AdvancedSettingsUiState { override val isAllowLanEnabled: Boolean = false, override val customDnsItems: List<CustomDnsItem> = listOf(), override val contentBlockersOptions: DefaultDnsOptions = DefaultDnsOptions(), - val stagedDns: StagedDns + val stagedDns: StagedDns, + override val selectedObfuscation: SelectedObfuscation = SelectedObfuscation.Off, ) : AdvancedSettingsUiState data class ContentBlockersInfoDialogUiState( @@ -42,7 +47,8 @@ sealed interface AdvancedSettingsUiState { override val isCustomDnsEnabled: Boolean = false, override val isAllowLanEnabled: Boolean = false, override val customDnsItems: List<CustomDnsItem> = listOf(), - override val contentBlockersOptions: DefaultDnsOptions = DefaultDnsOptions() + override val contentBlockersOptions: DefaultDnsOptions = DefaultDnsOptions(), + override val selectedObfuscation: SelectedObfuscation = SelectedObfuscation.Off, ) : AdvancedSettingsUiState data class CustomDnsInfoDialogUiState( @@ -50,7 +56,8 @@ sealed interface AdvancedSettingsUiState { override val isCustomDnsEnabled: Boolean = false, override val isAllowLanEnabled: Boolean = false, override val customDnsItems: List<CustomDnsItem> = listOf(), - override val contentBlockersOptions: DefaultDnsOptions = DefaultDnsOptions() + override val contentBlockersOptions: DefaultDnsOptions = DefaultDnsOptions(), + override val selectedObfuscation: SelectedObfuscation = SelectedObfuscation.Off, ) : AdvancedSettingsUiState data class MalwareInfoDialogUiState( @@ -58,6 +65,16 @@ sealed interface AdvancedSettingsUiState { override val isCustomDnsEnabled: Boolean = false, override val isAllowLanEnabled: Boolean = false, override val customDnsItems: List<CustomDnsItem> = listOf(), - override val contentBlockersOptions: DefaultDnsOptions = DefaultDnsOptions() + override val contentBlockersOptions: DefaultDnsOptions = DefaultDnsOptions(), + override val selectedObfuscation: SelectedObfuscation = SelectedObfuscation.Off, + ) : AdvancedSettingsUiState + + data class ObfuscationInfoDialogUiState( + override val mtu: String = "", + override val isCustomDnsEnabled: Boolean = false, + override val isAllowLanEnabled: Boolean = false, + override val customDnsItems: List<CustomDnsItem> = listOf(), + override val contentBlockersOptions: DefaultDnsOptions = DefaultDnsOptions(), + override val selectedObfuscation: SelectedObfuscation = SelectedObfuscation.Off, ) : AdvancedSettingsUiState } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ipc/Request.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ipc/Request.kt index 189dc27087..b46bfbf64b 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ipc/Request.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ipc/Request.kt @@ -83,7 +83,7 @@ sealed class Request : Message.RequestMessage() { @Parcelize data class SetDnsOptions(val dnsOptions: DnsOptions) : Request() - @Parcelize data class SetObfuscationSettings(val settings: ObfuscationSettings) : Request() + @Parcelize data class SetObfuscationSettings(val settings: ObfuscationSettings?) : Request() companion object { private const val MESSAGE_KEY = "request" diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/Udp2TcpObfuscationSettings.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/Udp2TcpObfuscationSettings.kt index 49f466a147..f01bb35c6f 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/Udp2TcpObfuscationSettings.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/Udp2TcpObfuscationSettings.kt @@ -3,7 +3,4 @@ package net.mullvad.mullvadvpn.model import android.os.Parcelable import kotlinx.parcelize.Parcelize -@Parcelize -data class Udp2TcpObfuscationSettings( - val port: Constraint<Int> -) : Parcelable +@Parcelize data class Udp2TcpObfuscationSettings(val port: Constraint<Int>) : Parcelable diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/repository/SettingsRepository.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/repository/SettingsRepository.kt index 1e00efc3c5..9ab6eb412f 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/repository/SettingsRepository.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/repository/SettingsRepository.kt @@ -13,6 +13,7 @@ import net.mullvad.mullvadvpn.model.CustomDnsOptions import net.mullvad.mullvadvpn.model.DefaultDnsOptions import net.mullvad.mullvadvpn.model.DnsOptions import net.mullvad.mullvadvpn.model.DnsState +import net.mullvad.mullvadvpn.model.ObfuscationSettings import net.mullvad.mullvadvpn.model.Settings import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnectionManager import net.mullvad.mullvadvpn.ui.serviceconnection.customDns @@ -56,4 +57,8 @@ class SettingsRepository( fun setWireguardMtu(value: Int?) { serviceConnectionManager.settingsListener()?.wireguardMtu = value } + + fun setObfuscationOptions(value: ObfuscationSettings) { + serviceConnectionManager.settingsListener()?.obfuscationSettings = value + } } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadDaemon.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadDaemon.kt index 86291f8c30..d46a71d805 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadDaemon.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadDaemon.kt @@ -174,7 +174,7 @@ class MullvadDaemon( updateRelaySettings(daemonInterfaceAddress, update) } - fun setObfuscationSettings(settings: ObfuscationSettings) { + fun setObfuscationSettings(settings: ObfuscationSettings?) { setObfuscationSettings(daemonInterfaceAddress, settings) } @@ -252,7 +252,7 @@ class MullvadDaemon( private external fun setObfuscationSettings( daemonInterfaceAddress: Long, - settings: ObfuscationSettings + settings: ObfuscationSettings? ) private fun notifyAppVersionInfoEvent(appVersionInfo: AppVersionInfo) { diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/SettingsListener.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/SettingsListener.kt index 2e688cda56..7606e7709c 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/SettingsListener.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/SettingsListener.kt @@ -17,7 +17,7 @@ class SettingsListener(endpoint: ServiceEndpoint) { class SetAllowLan(val allow: Boolean) : Command() class SetAutoConnect(val autoConnect: Boolean) : Command() class SetWireGuardMtu(val mtu: Int?) : Command() - class SetObfuscationSettings(val settings: ObfuscationSettings) : Command() + class SetObfuscationSettings(val settings: ObfuscationSettings?) : Command() } private val commandChannel = spawnActor() diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/AdvancedFragment.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/AdvancedFragment.kt index 5e9be88387..4bd7ea35b8 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/AdvancedFragment.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/AdvancedFragment.kt @@ -51,7 +51,9 @@ class AdvancedFragment : BaseFragment() { onDismissInfoClicked = vm::onDismissInfoClick, onBackClick = { activity?.onBackPressed() }, onStopEvent = vm::onStopEvent, - toastMessagesSharedFlow = vm.toastMessages + toastMessagesSharedFlow = vm.toastMessages, + onSelectObfuscationSetting = vm::onSelectObfuscationSetting, + onObfuscationInfoClicked = vm::onObfuscationInfoClicked ) } } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/SettingsListener.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/SettingsListener.kt index 1cb1cf2986..70e9721afc 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/SettingsListener.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/SettingsListener.kt @@ -5,6 +5,7 @@ import net.mullvad.mullvadvpn.ipc.Event import net.mullvad.mullvadvpn.ipc.EventDispatcher import net.mullvad.mullvadvpn.ipc.Request import net.mullvad.mullvadvpn.model.DnsOptions +import net.mullvad.mullvadvpn.model.ObfuscationSettings import net.mullvad.mullvadvpn.model.RelaySettings import net.mullvad.mullvadvpn.model.Settings import net.mullvad.talpid.util.EventNotifier @@ -34,6 +35,12 @@ class SettingsListener(private val connection: Messenger, eventDispatcher: Event connection.send(Request.SetWireGuardMtu(value).message) } + var obfuscationSettings: ObfuscationSettings? + get() = settingsNotifier.latestEvent?.obfuscationSettings + set(value) { + connection.send(Request.SetObfuscationSettings(value).message) + } + init { eventDispatcher.registerHandler(Event.SettingsUpdate::class, ::handleNewEvent) } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/AdvancedSettingsViewModel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/AdvancedSettingsViewModel.kt index 89f2df1399..1d353f5457 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/AdvancedSettingsViewModel.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/AdvancedSettingsViewModel.kt @@ -18,9 +18,13 @@ import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import net.mullvad.mullvadvpn.R import net.mullvad.mullvadvpn.compose.state.AdvancedSettingsUiState +import net.mullvad.mullvadvpn.model.Constraint import net.mullvad.mullvadvpn.model.DefaultDnsOptions import net.mullvad.mullvadvpn.model.DnsState +import net.mullvad.mullvadvpn.model.ObfuscationSettings +import net.mullvad.mullvadvpn.model.SelectedObfuscation import net.mullvad.mullvadvpn.model.Settings +import net.mullvad.mullvadvpn.model.Udp2TcpObfuscationSettings import net.mullvad.mullvadvpn.repository.SettingsRepository import net.mullvad.mullvadvpn.util.isValidMtu import org.apache.commons.validator.routines.InetAddressValidator @@ -47,6 +51,8 @@ class AdvancedSettingsViewModel( contentBlockersOptions = settings?.contentBlockersSettings() ?: DefaultDnsOptions(), isAllowLanEnabled = settings?.allowLan ?: false, + selectedObfuscation = settings?.selectedObfuscationSettings() + ?: SelectedObfuscation.Off, dialogState = dialogState ) } @@ -270,6 +276,21 @@ class AdvancedSettingsViewModel( } } + fun onSelectObfuscationSetting(selectedObfuscation: SelectedObfuscation) { + viewModelScope.launch(dispatcher) { + repository.setObfuscationOptions( + ObfuscationSettings( + selectedObfuscation = selectedObfuscation, + udp2tcp = Udp2TcpObfuscationSettings(Constraint.Any()) + ) + ) + } + } + + fun onObfuscationInfoClicked() { + dialogState.update { AdvancedSettingsDialogState.ObfuscationInfoDialog } + } + private fun updateDefaultDnsOptionsViaRepository(contentBlockersOption: DefaultDnsOptions) = viewModelScope.launch(dispatcher) { repository.setDnsOptions( @@ -312,6 +333,8 @@ class AdvancedSettingsViewModel( private fun Settings.contentBlockersSettings() = tunnelOptions.dnsOptions.defaultOptions + private fun Settings.selectedObfuscationSettings() = obfuscationSettings.selectedObfuscation + private fun String.isValidIp(): Boolean { return inetAddressValidator.isValid(this) } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/AdvancedSettingsViewModelState.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/AdvancedSettingsViewModelState.kt index fddcdfb5b7..3349e58b99 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/AdvancedSettingsViewModelState.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/AdvancedSettingsViewModelState.kt @@ -2,6 +2,7 @@ package net.mullvad.mullvadvpn.viewmodel import net.mullvad.mullvadvpn.compose.state.AdvancedSettingsUiState import net.mullvad.mullvadvpn.model.DefaultDnsOptions +import net.mullvad.mullvadvpn.model.SelectedObfuscation data class AdvancedSettingsViewModelState( val mtuValue: String, @@ -9,6 +10,7 @@ data class AdvancedSettingsViewModelState( val isAllowLanEnabled: Boolean, val customDnsList: List<CustomDnsItem>, val contentBlockersOptions: DefaultDnsOptions, + val selectedObfuscation: SelectedObfuscation, val dialogState: AdvancedSettingsDialogState ) { fun toUiState(): AdvancedSettingsUiState { @@ -20,7 +22,8 @@ data class AdvancedSettingsViewModelState( isAllowLanEnabled = isAllowLanEnabled, customDnsItems = customDnsList, contentBlockersOptions = contentBlockersOptions, - mtuEditValue = dialogState.mtuEditValue + mtuEditValue = dialogState.mtuEditValue, + selectedObfuscation = selectedObfuscation ) is AdvancedSettingsDialogState.DnsDialog -> AdvancedSettingsUiState.DnsDialogUiState( @@ -29,7 +32,8 @@ data class AdvancedSettingsViewModelState( isAllowLanEnabled = isAllowLanEnabled, customDnsItems = customDnsList, contentBlockersOptions = contentBlockersOptions, - stagedDns = dialogState.stagedDns + stagedDns = dialogState.stagedDns, + selectedObfuscation = selectedObfuscation ) is AdvancedSettingsDialogState.ContentBlockersInfoDialog -> AdvancedSettingsUiState.ContentBlockersInfoDialogUiState( @@ -37,7 +41,8 @@ data class AdvancedSettingsViewModelState( isCustomDnsEnabled = isCustomDnsEnabled, isAllowLanEnabled = isAllowLanEnabled, customDnsItems = customDnsList, - contentBlockersOptions = contentBlockersOptions + contentBlockersOptions = contentBlockersOptions, + selectedObfuscation = selectedObfuscation ) is AdvancedSettingsDialogState.CustomDnsInfoDialog -> AdvancedSettingsUiState.CustomDnsInfoDialogUiState( @@ -53,7 +58,17 @@ data class AdvancedSettingsViewModelState( isCustomDnsEnabled = isCustomDnsEnabled, isAllowLanEnabled = isAllowLanEnabled, customDnsItems = customDnsList, - contentBlockersOptions = contentBlockersOptions + contentBlockersOptions = contentBlockersOptions, + selectedObfuscation = selectedObfuscation + ) + is AdvancedSettingsDialogState.ObfuscationInfoDialog -> + AdvancedSettingsUiState.ObfuscationInfoDialogUiState( + mtu = mtuValue, + isCustomDnsEnabled = isCustomDnsEnabled, + isAllowLanEnabled = isAllowLanEnabled, + customDnsItems = customDnsList, + contentBlockersOptions = contentBlockersOptions, + selectedObfuscation = selectedObfuscation ) else -> AdvancedSettingsUiState.DefaultUiState( @@ -61,7 +76,8 @@ data class AdvancedSettingsViewModelState( isCustomDnsEnabled = isCustomDnsEnabled, isAllowLanEnabled = isAllowLanEnabled, customDnsItems = customDnsList, - contentBlockersOptions = contentBlockersOptions + contentBlockersOptions = contentBlockersOptions, + selectedObfuscation = selectedObfuscation ) } } @@ -76,7 +92,8 @@ data class AdvancedSettingsViewModelState( customDnsList = listOf(), contentBlockersOptions = DefaultDnsOptions(), isAllowLanEnabled = false, - dialogState = AdvancedSettingsDialogState.NoDialog + dialogState = AdvancedSettingsDialogState.NoDialog, + selectedObfuscation = SelectedObfuscation.Auto ) } } @@ -93,6 +110,8 @@ sealed class AdvancedSettingsDialogState { object CustomDnsInfoDialog : AdvancedSettingsDialogState() object MalwareInfoDialog : AdvancedSettingsDialogState() + + object ObfuscationInfoDialog : AdvancedSettingsDialogState() } sealed interface StagedDns { diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml index d007411148..65e0295a45 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml @@ -187,4 +187,11 @@ <string name="settings_changes_effect_warning_short">DNS settings might not go into effect immediately</string> <string name="settings_changes_effect_warning_content_blocker">Changes to DNS related settings might not go into effect immediately due to cached results.</string> <string name="manage_account">Manage account</string> + <string name="obfuscation_title">Obfuscation</string> + <string name="obfuscation_info">Obfuscation hides the WireGuard traffic inside another protocol. It can be used to help circumvent censorship and other types of filtering, where a plain WireGuard connect would be blocked.</string> + <string name="obfuscation_on_udp_over_tcp">On (UDP-over-TCP)</string> + <string name="automatic">Automatic</string> + <string name="off">Off</string> + <string name="udp_over_tcp_port">UDP-over-TCP port</string> + <string name="udp_over_tcp_port_info">Which TCP port the UDP-over-TCP obfuscation protocol should connect to on the VPN server.</string> </resources> |
