summaryrefslogtreecommitdiffhomepage
path: root/android/app/src
diff options
context:
space:
mode:
authorJonatan Rhodin <jonatan.rhodin@mullvad.net>2023-04-27 07:59:07 +0200
committerJonatan Rhodin <jonatan.rhodin@mullvad.net>2023-05-09 16:18:13 +0200
commit9638d005d4da49eab3f94c3c3ae6b4d9637f9a3b (patch)
treec28273fa29f45a83e4086ae7b5ca4e994aab87e8 /android/app/src
parent45a4e25733e305eed244a3ebd1a1e88b3d5cb361 (diff)
downloadmullvadvpn-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')
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/BaseCell.kt4
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/InformationComposeCell.kt81
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/SwitchComposeCell.kt2
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/ObfuscationInfoDialog.kt10
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/UdpOverTcpPortInfoDialog.kt13
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/AdvancedSettingScreen.kt73
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/AdvancedSettingsUiState.kt29
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/ipc/Request.kt2
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/Udp2TcpObfuscationSettings.kt5
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/repository/SettingsRepository.kt5
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadDaemon.kt4
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/SettingsListener.kt2
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/AdvancedFragment.kt4
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/SettingsListener.kt7
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/AdvancedSettingsViewModel.kt23
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/AdvancedSettingsViewModelState.kt31
-rw-r--r--android/app/src/main/res/values/strings.xml7
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>