From c71678411a0904497d93328f85b9a842a8908af7 Mon Sep 17 00:00:00 2001 From: Albin Date: Mon, 2 Sep 2024 14:45:54 +0200 Subject: [PATCH 1/6] Bump wireguard-go submodule --- wireguard-go-rs/libwg/wireguard-go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wireguard-go-rs/libwg/wireguard-go b/wireguard-go-rs/libwg/wireguard-go index de174ac6de29..265d73245fad 160000 --- a/wireguard-go-rs/libwg/wireguard-go +++ b/wireguard-go-rs/libwg/wireguard-go @@ -1 +1 @@ -Subproject commit de174ac6de2934dc82d9b8301c17de87cbd575f3 +Subproject commit 265d73245fad6e313f153e031445cafa203c5b4a From 21a12a81e7b8af21abe7c326e190cf0d694f3f93 Mon Sep 17 00:00:00 2001 From: Albin Date: Fri, 23 Aug 2024 16:19:49 +0200 Subject: [PATCH 2/6] Check out wireguard-go-rs recursively in workflow --- .github/workflows/android-app.yml | 5 +++++ .github/workflows/clippy.yml | 2 +- .github/workflows/rust-unused-dependencies.yml | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/.github/workflows/android-app.yml b/.github/workflows/android-app.yml index 436d53f93d85..239f8d0931a4 100644 --- a/.github/workflows/android-app.yml +++ b/.github/workflows/android-app.yml @@ -175,6 +175,11 @@ jobs: with: submodules: true + - name: Checkout wireguard-go-rs recursively + run: | + git config --global --add safe.directory '*' + git submodule update --init --recursive --depth=1 wireguard-go-rs + - name: Calculate native lib cache hash id: native-lib-cache-hash shell: bash diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index 55de67775339..d847602fb60a 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -97,7 +97,7 @@ jobs: - name: Checkout wireguard-go submodule run: | git config --global --add safe.directory '*' - git submodule update --init --depth=1 wireguard-go-rs + git submodule update --init --recursive --depth=1 wireguard-go-rs - name: Clippy check env: diff --git a/.github/workflows/rust-unused-dependencies.yml b/.github/workflows/rust-unused-dependencies.yml index 3122ce5d03c9..cfdd75e75176 100644 --- a/.github/workflows/rust-unused-dependencies.yml +++ b/.github/workflows/rust-unused-dependencies.yml @@ -79,7 +79,7 @@ jobs: - name: Checkout wireguard-go submodule run: | git config --global --add safe.directory '*' - git submodule update --init --depth=1 wireguard-go-rs + git submodule update --init --recursive --depth=1 wireguard-go-rs - name: Install nightly Rust toolchain run: | From d464325f98bc488f091ef18b4ba04e0d7dbe2605 Mon Sep 17 00:00:00 2001 From: Albin Date: Thu, 29 Aug 2024 13:00:11 +0200 Subject: [PATCH 3/6] Build wg with daita support for android --- mullvad-daemon/build.rs | 7 ++----- mullvad-management-interface/build.rs | 8 ++------ mullvad-management-interface/src/client.rs | 2 +- mullvad-relay-selector/build.rs | 8 ++------ mullvad-types/build.rs | 8 ++------ talpid-types/build.rs | 8 ++------ talpid-wireguard/build.rs | 6 ++---- wireguard-go-rs/Cargo.toml | 2 +- wireguard-go-rs/build.rs | 9 +++++++-- wireguard-go-rs/libwg/Android.mk | 8 ++++++-- 10 files changed, 27 insertions(+), 39 deletions(-) diff --git a/mullvad-daemon/build.rs b/mullvad-daemon/build.rs index f89394a688b6..1310450aa919 100644 --- a/mullvad-daemon/build.rs +++ b/mullvad-daemon/build.rs @@ -29,13 +29,10 @@ fn main() { } res.compile().expect("Unable to generate windows resources"); } - let target_os = std::env::var("CARGO_CFG_TARGET_OS").expect("CARGO_CFG_TARGET_OS not set"); - // Enable DAITA by default on desktop + // Enable DAITA by default on desktop and android println!("cargo::rustc-check-cfg=cfg(daita)"); - if let "linux" | "windows" | "macos" = target_os.as_str() { - println!(r#"cargo::rustc-cfg=daita"#); - } + println!(r#"cargo::rustc-cfg=daita"#); } fn commit_date() -> String { diff --git a/mullvad-management-interface/build.rs b/mullvad-management-interface/build.rs index 569d3379cc78..09f8601a83e3 100644 --- a/mullvad-management-interface/build.rs +++ b/mullvad-management-interface/build.rs @@ -1,11 +1,7 @@ fn main() { tonic_build::compile_protos("proto/management_interface.proto").unwrap(); - let target_os = std::env::var("CARGO_CFG_TARGET_OS").expect("CARGO_CFG_TARGET_OS not set"); - - // Enable DAITA by default on desktop + // Enable DAITA by default on desktop and android println!("cargo::rustc-check-cfg=cfg(daita)"); - if let "linux" | "windows" | "macos" = target_os.as_str() { - println!(r#"cargo::rustc-cfg=daita"#); - } + println!(r#"cargo::rustc-cfg=daita"#); } diff --git a/mullvad-management-interface/src/client.rs b/mullvad-management-interface/src/client.rs index b0876093fa2a..3b2cabc33ea7 100644 --- a/mullvad-management-interface/src/client.rs +++ b/mullvad-management-interface/src/client.rs @@ -3,7 +3,7 @@ use crate::types; #[cfg(not(target_os = "android"))] use futures::{Stream, StreamExt}; -#[cfg(daita)] +#[cfg(all(daita, not(target_os = "android")))] use mullvad_types::wireguard::DaitaSettings; use mullvad_types::{ access_method::AccessMethodSetting, diff --git a/mullvad-relay-selector/build.rs b/mullvad-relay-selector/build.rs index 678311e004e4..810204ba1032 100644 --- a/mullvad-relay-selector/build.rs +++ b/mullvad-relay-selector/build.rs @@ -1,9 +1,5 @@ fn main() { - let target_os = std::env::var("CARGO_CFG_TARGET_OS").expect("CARGO_CFG_TARGET_OS not set"); - - // Enable DAITA by default on desktop + // Enable DAITA by default on desktop and android println!("cargo::rustc-check-cfg=cfg(daita)"); - if let "linux" | "windows" | "macos" = target_os.as_str() { - println!(r#"cargo::rustc-cfg=daita"#); - } + println!(r#"cargo::rustc-cfg=daita"#); } diff --git a/mullvad-types/build.rs b/mullvad-types/build.rs index 678311e004e4..810204ba1032 100644 --- a/mullvad-types/build.rs +++ b/mullvad-types/build.rs @@ -1,9 +1,5 @@ fn main() { - let target_os = std::env::var("CARGO_CFG_TARGET_OS").expect("CARGO_CFG_TARGET_OS not set"); - - // Enable DAITA by default on desktop + // Enable DAITA by default on desktop and android println!("cargo::rustc-check-cfg=cfg(daita)"); - if let "linux" | "windows" | "macos" = target_os.as_str() { - println!(r#"cargo::rustc-cfg=daita"#); - } + println!(r#"cargo::rustc-cfg=daita"#); } diff --git a/talpid-types/build.rs b/talpid-types/build.rs index 678311e004e4..810204ba1032 100644 --- a/talpid-types/build.rs +++ b/talpid-types/build.rs @@ -1,9 +1,5 @@ fn main() { - let target_os = std::env::var("CARGO_CFG_TARGET_OS").expect("CARGO_CFG_TARGET_OS not set"); - - // Enable DAITA by default on desktop + // Enable DAITA by default on desktop and android println!("cargo::rustc-check-cfg=cfg(daita)"); - if let "linux" | "windows" | "macos" = target_os.as_str() { - println!(r#"cargo::rustc-cfg=daita"#); - } + println!(r#"cargo::rustc-cfg=daita"#); } diff --git a/talpid-wireguard/build.rs b/talpid-wireguard/build.rs index 1eb322606160..7d38f75118d2 100644 --- a/talpid-wireguard/build.rs +++ b/talpid-wireguard/build.rs @@ -14,11 +14,9 @@ fn add_wireguard_go_cfg(target_os: &str) { println!("cargo::rustc-cfg=wireguard_go"); } - // Enable DAITA by default on desktop + // Enable DAITA by default on desktop and android println!("cargo::rustc-check-cfg=cfg(daita)"); - if matches!(target_os, "linux" | "macos" | "windows") { - println!(r#"cargo::rustc-cfg=daita"#); - } + println!(r#"cargo::rustc-cfg=daita"#); } fn declare_libs_dir(base: &str) { diff --git a/wireguard-go-rs/Cargo.toml b/wireguard-go-rs/Cargo.toml index 062c80de34d2..e8a97ad12b0e 100644 --- a/wireguard-go-rs/Cargo.toml +++ b/wireguard-go-rs/Cargo.toml @@ -12,7 +12,7 @@ thiserror.workspace = true log.workspace = true zeroize = "1.8.1" -[target.'cfg(any(target_os = "linux", target_os = "macos"))'.dependencies] +[target.'cfg(not(target_os = "windows"))'.dependencies] # The app does not depend on maybenot-ffi itself, but adds it as a dependency to expose FFI symbols to wireguard-go. # This is done, instead of using the makefile in wireguard-go to build maybenot-ffi into its archive, to prevent # name clashes induced by link-time optimization. diff --git a/wireguard-go-rs/build.rs b/wireguard-go-rs/build.rs index da981770d0bf..5c7feebb6bf9 100644 --- a/wireguard-go-rs/build.rs +++ b/wireguard-go-rs/build.rs @@ -20,7 +20,7 @@ fn main() -> anyhow::Result<()> { match target_os.as_str() { "linux" => build_static_lib(Os::Linux, true)?, "macos" => build_static_lib(Os::MacOs, true)?, - "android" => build_android_dynamic_lib(false)?, + "android" => build_android_dynamic_lib(true)?, // building wireguard-go-rs for windows is not implemented _ => {} } @@ -192,7 +192,12 @@ fn build_android_dynamic_lib(daita: bool) -> anyhow::Result<()> { .env("ANDROID_ARCH_NAME", android_arch_name(target)) .env("GOPATH", &go_path) // Note: -w -s results in a stripped binary - .env("LDFLAGS", format!("-L{out_dir} -w -s")); + .env("LDFLAGS", format!("-L{out_dir} -w -s")) + // Note: the build container overrides CARGO_TARGET_DIR, which will cause problems + // since we will spawn another cargo process as part of building maybenot (which we + // link into libwg). A work around is to simply override the overridden value, and we + // do this by pointing to a target folder in our temporary build folder. + .env("CARGO_TARGET_DIR", tmp_build_dir.join("target")); exec(build_command)?; diff --git a/wireguard-go-rs/libwg/Android.mk b/wireguard-go-rs/libwg/Android.mk index 9cb87b24717b..f847aa66d276 100644 --- a/wireguard-go-rs/libwg/Android.mk +++ b/wireguard-go-rs/libwg/Android.mk @@ -29,11 +29,15 @@ GOBUILDVERSION := 1.21.3 GOBUILDTARBALL := https://go.dev/dl/go$(GOBUILDVERSION).$(GOBUILDOS)-$(GOBUILDARCH).tar.gz GOBUILDVERSION_NEEDED := go version go$(GOBUILDVERSION) $(GOBUILDOS)/$(GOBUILDARCH) + $(DESTDIR)/libwg.so: mkdir -p $(DESTDIR) - go get -tags "linux android" + # Build libmaybenot + make --directory wireguard-go libmaybenot.a LIBDEST="$(DESTDIR)" TARGET="$(TARGET)" CARGO_TARGET_DIR="$(CARGO_TARGET_DIR)" + # Build wireguard-go + go get -tags "linux android daita" chmod -fR +w "$(GOPATH)/pkg/mod" - go build -tags "linux android" -ldflags="-X main.socketDirectory=/data/data/$(ANDROID_PACKAGE_NAME)/cache/wireguard" -v -o "$@" -buildmode c-shared + go build -tags "linux android daita" -ldflags="-X main.socketDirectory=/data/data/$(ANDROID_PACKAGE_NAME)/cache/wireguard" -v -o "$@" -buildmode c-shared rm -f $(DESTDIR)/libwg.h From 3847c1eb82a324a4b5fff2a5e076750ebf4449c4 Mon Sep 17 00:00:00 2001 From: Albin Date: Mon, 26 Aug 2024 15:48:37 +0200 Subject: [PATCH 4/6] Add daita grpc and ui --- .github/workflows/android-app.yml | 3 + android/app/build.gradle.kts | 10 ++ .../compose/data/DummyRelayItems.kt | 2 + .../mullvadvpn/compose/cell/FilterRow.kt | 12 +++ .../compose/component/FilterChip.kt | 37 +++++-- .../compose/component/LocationInfo.kt | 50 +++++++-- .../compose/dialog/DaitaConfirmationDialog.kt | 101 ++++++++++++++++++ .../compose/dialog/DaitaInfoDialog.kt | 35 ++++++ .../compose/preview/RelayItemPreviewData.kt | 2 + .../compose/preview/TunnelStatePreviewData.kt | 7 +- .../compose/screen/ConnectScreen.kt | 1 + .../compose/screen/VpnSettingsScreen.kt | 34 +++++- .../compose/state/SelectLocationUiState.kt | 2 + .../compose/state/VpnSettingsUiState.kt | 3 + .../net/mullvad/mullvadvpn/di/UiModule.kt | 8 +- .../relaylist/RelayItemExtensions.kt | 31 ++++-- .../repository/SettingsRepository.kt | 2 + .../usecase/FilteredRelayListUseCase.kt | 18 +++- .../FilterCustomListsRelayItemUseCase.kt | 16 ++- .../viewmodel/SelectLocationViewModel.kt | 9 +- .../viewmodel/VpnSettingsViewModel.kt | 9 ++ .../viewmodel/VpnSettingsViewModelState.kt | 3 + .../CustomListLocationsViewModelTest.kt | 1 + .../viewmodel/SelectLocationViewModelTest.kt | 7 ++ .../viewmodel/VpnSettingsViewModelTest.kt | 7 +- .../lib/daemon/grpc/ManagementService.kt | 10 ++ .../lib/daemon/grpc/mapper/ToDomain.kt | 14 ++- .../daemon/grpc/RelayNameComparatorTest.kt | 20 ++++ .../mullvadvpn/lib/model/FeatureIndicator.kt | 2 +- .../mullvad/mullvadvpn/lib/model/RelayItem.kt | 1 + .../lib/model/SetDaitaSettingsError.kt | 5 + .../mullvad/mullvadvpn/lib/model/Settings.kt | 2 + .../mullvadvpn/lib/model/TunnelEndpoint.kt | 1 + .../mullvadvpn/lib/model/TunnelState.kt | 8 ++ .../lib/model/WireguardRelayEndpointData.kt | 3 + .../lib/model/WireguardTunnelOptions.kt | 6 +- .../resource/src/main/res/values/strings.xml | 6 ++ .../res/values/strings_non_translatable.xml | 2 + .../service/FileResourceExtractor.kt | 23 ---- .../mullvadvpn/service/MullvadVpnService.kt | 18 ++-- .../service/util/ContextExtensions.kt | 23 ++++ build-apk.sh | 3 + 42 files changed, 473 insertions(+), 84 deletions(-) create mode 100644 android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/DaitaConfirmationDialog.kt create mode 100644 android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/DaitaInfoDialog.kt create mode 100644 android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/SetDaitaSettingsError.kt create mode 100644 android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/WireguardRelayEndpointData.kt delete mode 100644 android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/FileResourceExtractor.kt create mode 100644 android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/util/ContextExtensions.kt diff --git a/.github/workflows/android-app.yml b/.github/workflows/android-app.yml index 239f8d0931a4..204b84ed5a40 100644 --- a/.github/workflows/android-app.yml +++ b/.github/workflows/android-app.yml @@ -324,6 +324,9 @@ jobs: name: relay-list path: android/app/build/extraAssets + - name: Copy maybenot machines to asset directory + run: cp dist-assets/maybenot_machines android/app/build/extraAssets/maybenot_machines + - name: Build app uses: burrunan/gradle-cache-action@v1 with: diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts index f8c92c9849a8..f5e4c22d6bf5 100644 --- a/android/app/build.gradle.kts +++ b/android/app/build.gradle.kts @@ -20,6 +20,7 @@ plugins { val repoRootPath = rootProject.projectDir.absoluteFile.parentFile.absolutePath val extraAssetsDirectory = "${project.buildDir}/extraAssets" val relayListPath = "$extraAssetsDirectory/relays.json" +val maybenotMachinesDirectory = "$extraAssetsDirectory/maybenot_machines" val defaultChangelogAssetsDirectory = "$repoRootPath/android/src/main/play/release-notes/" val extraJniDirectory = "${project.buildDir}/extraJni" @@ -239,6 +240,7 @@ android { // Ensure all relevant assemble tasks depend on our ensure tasks. tasks.get("assemble$capitalizedVariantName").apply { dependsOn(tasks.get("ensureRelayListExist")) + dependsOn(tasks.get("ensureMaybenotMachinesExist")) dependsOn(tasks.get("ensureJniDirectoryExist")) dependsOn(tasks.get("ensureValidVersionCode")) } @@ -283,6 +285,14 @@ tasks.register("ensureRelayListExist") { } } +tasks.register("ensureMaybenotMachinesExist") { + doLast { + if (!file(maybenotMachinesDirectory).exists()) { + throw GradleException("Missing maybenot machines: $maybenotMachinesDirectory") + } + } +} + tasks.register("ensureJniDirectoryExist") { doLast { if (!file(extraJniDirectory).exists()) { diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/data/DummyRelayItems.kt b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/data/DummyRelayItems.kt index 3cea5e5c681b..a073bc60ff55 100644 --- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/data/DummyRelayItems.kt +++ b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/data/DummyRelayItems.kt @@ -22,6 +22,7 @@ private val DUMMY_RELAY_1 = active = true, provider = Provider(providerId = ProviderId("PROVIDER RENTED"), ownership = Ownership.Rented), + daita = false, ) private val DUMMY_RELAY_2 = RelayItem.Location.Relay( @@ -33,6 +34,7 @@ private val DUMMY_RELAY_2 = active = true, provider = Provider(providerId = ProviderId("PROVIDER OWNED"), ownership = Ownership.MullvadOwned), + daita = false, ) private val DUMMY_RELAY_CITY_1 = RelayItem.Location.City( diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/FilterRow.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/FilterRow.kt index a94846dacff1..d3e233c67b75 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/FilterRow.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/FilterRow.kt @@ -57,6 +57,7 @@ fun FilterRow( is FilterChip.Ownership -> OwnershipFilterChip(it.ownership, onRemoveOwnershipFilter) is FilterChip.Provider -> ProviderFilterChip(it.count, onRemoveProviderFilter) + is FilterChip.Daita -> DaitaFilterChip() } } } @@ -67,6 +68,7 @@ fun ProviderFilterChip(providers: Int, onRemoveClick: () -> Unit) { MullvadFilterChip( text = stringResource(id = R.string.number_of_providers, providers), onRemoveClick = onRemoveClick, + enabled = true, ) } @@ -75,6 +77,16 @@ fun OwnershipFilterChip(ownership: Ownership, onRemoveClick: () -> Unit) { MullvadFilterChip( text = stringResource(ownership.stringResources()), onRemoveClick = onRemoveClick, + enabled = true, + ) +} + +@Composable +fun DaitaFilterChip() { + MullvadFilterChip( + text = stringResource(id = R.string.setting_chip, stringResource(id = R.string.daita)), + onRemoveClick = {}, + enabled = false, ) } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/FilterChip.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/FilterChip.kt index 4761c15c9df5..94e1c7853ace 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/FilterChip.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/FilterChip.kt @@ -19,11 +19,24 @@ import net.mullvad.mullvadvpn.lib.theme.shape.chipShape @Preview @Composable -private fun PreviewMullvadFilterChip() { +private fun PreviewEnabledMullvadFilterChip() { AppTheme { MullvadFilterChip( text = stringResource(id = R.string.number_of_providers), onRemoveClick = {}, + enabled = true, + ) + } +} + +@Preview +@Composable +private fun PreviewDisabledMullvadFilterChip() { + AppTheme { + MullvadFilterChip( + text = stringResource(id = R.string.number_of_providers), + onRemoveClick = {}, + enabled = false, ) } } @@ -36,30 +49,38 @@ fun MullvadFilterChip( iconColor: Color = MaterialTheme.colorScheme.onPrimary, text: String, onRemoveClick: () -> Unit, + enabled: Boolean, ) { InputChip( + enabled = enabled, shape = MaterialTheme.shapes.chipShape, colors = FilterChipDefaults.filterChipColors( containerColor = containerColor, + disabledContainerColor = containerColor, labelColor = labelColor, + disabledLabelColor = labelColor, iconColor = iconColor, ), border = FilterChipDefaults.filterChipBorder( borderColor = borderColor, + disabledBorderColor = borderColor, enabled = true, selected = false, ), selected = false, onClick = onRemoveClick, label = { Text(text = text, style = MaterialTheme.typography.labelMedium) }, - trailingIcon = { - Icon( - painter = painterResource(id = R.drawable.icon_close), - contentDescription = null, - modifier = Modifier.size(Dimens.smallIconSize), - ) - }, + trailingIcon = + if (enabled) { + { + Icon( + painter = painterResource(id = R.drawable.icon_close), + contentDescription = null, + modifier = Modifier.size(Dimens.smallIconSize), + ) + } + } else null, ) } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/LocationInfo.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/LocationInfo.kt index f9c0f26862ea..3fbd40c53770 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/LocationInfo.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/LocationInfo.kt @@ -33,6 +33,7 @@ private fun PreviewLocationInfo() { isVisible = true, isExpanded = true, location = null, + isUsingDaita = false, inAddress = null, outAddress = "", ) @@ -48,6 +49,7 @@ fun LocationInfo( isVisible: Boolean, isExpanded: Boolean, location: GeoIpLocation?, + isUsingDaita: Boolean, inAddress: Triple?, outAddress: String, ) { @@ -61,15 +63,12 @@ fun LocationInfo( .then(modifier) ) { Row(verticalAlignment = Alignment.CenterVertically) { - Text( - text = location?.hostname ?: "", - color = - if (isExpanded) { - colorExpanded - } else { - colorCollapsed - }, - style = MaterialTheme.typography.labelLarge.copy(fontWeight = FontWeight.SemiBold), + RelayHostname( + hostname = location?.hostname, + isUsingDaita = isUsingDaita, + isExpanded = isExpanded, + colorExpanded = colorExpanded, + colorCollapsed = colorCollapsed, ) Chevron( isExpanded = isExpanded, @@ -119,3 +118,36 @@ fun LocationInfo( ) } } + +@Composable +private fun RelayHostname( + hostname: String?, + isUsingDaita: Boolean, + isExpanded: Boolean, + colorExpanded: Color, + colorCollapsed: Color, +) { + val hostnameTitle = + when { + hostname != null && isUsingDaita -> { + stringResource( + id = R.string.connected_using_daita, + hostname, + stringResource(id = R.string.daita), + ) + } + hostname != null -> hostname + else -> "" + } + + Text( + text = hostnameTitle, + color = + if (isExpanded) { + colorExpanded + } else { + colorCollapsed + }, + style = MaterialTheme.typography.labelLarge.copy(fontWeight = FontWeight.SemiBold), + ) +} diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/DaitaConfirmationDialog.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/DaitaConfirmationDialog.kt new file mode 100644 index 000000000000..a5237a0ca5ca --- /dev/null +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/DaitaConfirmationDialog.kt @@ -0,0 +1,101 @@ +package net.mullvad.mullvadvpn.compose.dialog + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.AlertDialog +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.lifecycle.compose.dropUnlessResumed +import com.ramcosta.composedestinations.annotation.Destination +import com.ramcosta.composedestinations.annotation.RootGraph +import com.ramcosta.composedestinations.result.EmptyResultBackNavigator +import com.ramcosta.composedestinations.result.ResultBackNavigator +import com.ramcosta.composedestinations.spec.DestinationStyle +import net.mullvad.mullvadvpn.R +import net.mullvad.mullvadvpn.compose.button.PrimaryButton +import net.mullvad.mullvadvpn.compose.component.drawVerticalScrollbar +import net.mullvad.mullvadvpn.lib.theme.AppTheme +import net.mullvad.mullvadvpn.lib.theme.Dimens +import net.mullvad.mullvadvpn.lib.theme.color.AlphaScrollbar + +@Preview +@Composable +private fun PreviewDaitaConfirmationDialog() { + AppTheme { DaitaConfirmation(EmptyResultBackNavigator()) } +} + +@Destination(style = DestinationStyle.Dialog::class) +@Composable +fun DaitaConfirmation(navigator: ResultBackNavigator) { + AlertDialog( + onDismissRequest = dropUnlessResumed { navigator.navigateBack(false) }, + icon = { + Icon( + modifier = Modifier.fillMaxWidth().height(Dimens.dialogIconHeight), + painter = painterResource(id = R.drawable.icon_alert), + contentDescription = null, + tint = MaterialTheme.colorScheme.onSurface, + ) + }, + text = { + val scrollState = rememberScrollState() + Column( + Modifier.drawVerticalScrollbar( + scrollState, + MaterialTheme.colorScheme.onPrimary.copy(alpha = AlphaScrollbar), + ) + .verticalScroll(scrollState), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Text( + text = stringResource(id = R.string.daita_relay_subset_warning), + color = MaterialTheme.colorScheme.onSurface, + style = MaterialTheme.typography.bodySmall, + modifier = Modifier.fillMaxWidth(), + ) + + Spacer(modifier = Modifier.height(Dimens.verticalSpace)) + + Text( + text = + stringResource( + id = R.string.daita_warning, + stringResource(id = R.string.daita), + ), + color = MaterialTheme.colorScheme.onSurface, + style = MaterialTheme.typography.bodySmall, + modifier = Modifier.fillMaxWidth(), + ) + } + }, + confirmButton = { + Column(verticalArrangement = Arrangement.spacedBy(Dimens.buttonSpacing)) { + PrimaryButton( + modifier = Modifier.fillMaxWidth(), + text = stringResource(R.string.enable_anyway), + onClick = { navigator.navigateBack(true) }, + ) + + PrimaryButton( + modifier = Modifier.fillMaxWidth(), + text = stringResource(R.string.back), + onClick = dropUnlessResumed { navigator.navigateBack(false) }, + ) + } + }, + containerColor = MaterialTheme.colorScheme.surface, + titleContentColor = MaterialTheme.colorScheme.onSurface, + ) +} diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/DaitaInfoDialog.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/DaitaInfoDialog.kt new file mode 100644 index 000000000000..a7e2b0b78ebd --- /dev/null +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/DaitaInfoDialog.kt @@ -0,0 +1,35 @@ +package net.mullvad.mullvadvpn.compose.dialog + +import androidx.compose.runtime.Composable +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.lifecycle.compose.dropUnlessResumed +import com.ramcosta.composedestinations.annotation.Destination +import com.ramcosta.composedestinations.annotation.RootGraph +import com.ramcosta.composedestinations.navigation.DestinationsNavigator +import com.ramcosta.composedestinations.navigation.EmptyDestinationsNavigator +import com.ramcosta.composedestinations.spec.DestinationStyle +import net.mullvad.mullvadvpn.R +import net.mullvad.mullvadvpn.lib.theme.AppTheme + +@Preview +@Composable +private fun PreviewDaitaInfoDialog() { + AppTheme { DaitaInfo(EmptyDestinationsNavigator) } +} + +@Destination(style = DestinationStyle.Dialog::class) +@Composable +fun DaitaInfo(navigator: DestinationsNavigator) { + InfoDialog( + message = + stringResource( + id = R.string.daita_info, + stringResource(id = R.string.daita), + stringResource(id = R.string.daita_full), + ), + additionalInfo = + stringResource(id = R.string.daita_warning, stringResource(id = R.string.daita)), + onDismiss = dropUnlessResumed { navigator.navigateUp() }, + ) +} diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/preview/RelayItemPreviewData.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/preview/RelayItemPreviewData.kt index 70d707c10cdc..0af4199f40ed 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/preview/RelayItemPreviewData.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/preview/RelayItemPreviewData.kt @@ -51,11 +51,13 @@ private fun generateRelayItemRelay( cityCode: GeoLocationId.City, hostName: String, active: Boolean = true, + daita: Boolean = true, ) = RelayItem.Location.Relay( id = GeoLocationId.Hostname(city = cityCode, code = hostName), active = active, provider = Provider(ProviderId("Provider"), Ownership.MullvadOwned), + daita = daita, ) private fun String.generateCountryCode() = 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 index a8981e612ef1..b6ca18c91a7c 100644 --- 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 @@ -18,14 +18,14 @@ object TunnelStatePreviewData { fun generateConnectingState(featureIndicators: Int, quantumResistant: Boolean) = TunnelState.Connecting( - endpoint = generateTunnelEndpoint(quantumResistant = quantumResistant), + endpoint = generateTunnelEndpoint(quantumResistant = quantumResistant, daita = false), location = generateLocation(), featureIndicators = generateFeatureIndicators(featureIndicators), ) fun generateConnectedState(featureIndicators: Int, quantumResistant: Boolean) = TunnelState.Connected( - endpoint = generateTunnelEndpoint(quantumResistant = quantumResistant), + endpoint = generateTunnelEndpoint(quantumResistant = quantumResistant, daita = true), location = generateLocation(), featureIndicators = generateFeatureIndicators(featureIndicators), ) @@ -39,7 +39,7 @@ object TunnelStatePreviewData { ) } -private fun generateTunnelEndpoint(quantumResistant: Boolean): TunnelEndpoint = +private fun generateTunnelEndpoint(quantumResistant: Boolean, daita: Boolean): TunnelEndpoint = TunnelEndpoint( endpoint = generateEndpoint(TransportProtocol.Udp), quantumResistant = quantumResistant, @@ -48,6 +48,7 @@ private fun generateTunnelEndpoint(quantumResistant: Boolean): TunnelEndpoint = endpoint = generateEndpoint(TransportProtocol.Tcp), ObfuscationType.Udp2Tcp, ), + daita = daita, ) private fun generateEndpoint(transportProtocol: TransportProtocol) = diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/ConnectScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/ConnectScreen.kt index c5b2be56ed0d..c9ee962eb11d 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/ConnectScreen.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/ConnectScreen.kt @@ -356,6 +356,7 @@ private fun ConnectionInfo(state: ConnectUiState) { isVisible = state.showLocationInfo, isExpanded = expanded, location = state.location, + isUsingDaita = state.tunnelState.isUsingDaita(), inAddress = state.inAddress, outAddress = state.outAddress, modifier = diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/VpnSettingsScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/VpnSettingsScreen.kt index 1bccbaa713c0..c130d6d61cc2 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/VpnSettingsScreen.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/VpnSettingsScreen.kt @@ -36,6 +36,8 @@ import com.ramcosta.composedestinations.annotation.RootGraph import com.ramcosta.composedestinations.generated.destinations.AutoConnectAndLockdownModeDestination import com.ramcosta.composedestinations.generated.destinations.ContentBlockersInfoDestination import com.ramcosta.composedestinations.generated.destinations.CustomDnsInfoDestination +import com.ramcosta.composedestinations.generated.destinations.DaitaConfirmationDestination +import com.ramcosta.composedestinations.generated.destinations.DaitaInfoDestination import com.ramcosta.composedestinations.generated.destinations.DnsDestination import com.ramcosta.composedestinations.generated.destinations.LocalNetworkSharingInfoDestination import com.ramcosta.composedestinations.generated.destinations.MalwareInfoDestination @@ -147,6 +149,7 @@ fun VpnSettings( dnsDialogResult: ResultRecipient, customWgPortResult: ResultRecipient, mtuDialogResult: ResultRecipient, + daitaConfirmationDialogResult: ResultRecipient, ) { val vm = koinViewModel() val state by vm.uiState.collectAsStateWithLifecycle() @@ -176,6 +179,12 @@ fun VpnSettings( } } + daitaConfirmationDialogResult.OnNavResultValue { doEnableDaita -> + if (doEnableDaita) { + vm.onToggleDaita(true) + } + } + val snackbarHostState = remember { SnackbarHostState() } val context = LocalContext.current CollectSideEffectWithLifecycle(vm.uiSideEffect) { @@ -224,6 +233,9 @@ fun VpnSettings( }, navigateToLocalNetworkSharingInfo = dropUnlessResumed { navigator.navigate(LocalNetworkSharingInfoDestination) }, + navigateToDaitaInfo = dropUnlessResumed { navigator.navigate(DaitaInfoDestination) }, + navigateToDaitaConfirmation = + dropUnlessResumed { navigator.navigate(DaitaConfirmationDestination) }, navigateToServerIpOverrides = dropUnlessResumed { navigator.navigate(ServerIpOverridesDestination) }, onToggleBlockTrackers = vm::onToggleBlockTrackers, @@ -231,6 +243,7 @@ fun VpnSettings( onToggleBlockMalware = vm::onToggleBlockMalware, onToggleAutoConnect = vm::onToggleAutoConnect, onToggleLocalNetworkSharing = vm::onToggleLocalNetworkSharing, + onDisableDaita = { vm.onToggleDaita(false) }, onToggleBlockAdultContent = vm::onToggleBlockAdultContent, onToggleBlockGambling = vm::onToggleBlockGambling, onToggleBlockSocialMedia = vm::onToggleBlockSocialMedia, @@ -273,6 +286,8 @@ fun VpnSettingsScreen( navigateUdp2TcpInfo: () -> Unit = {}, navigateToWireguardPortInfo: (availablePortRanges: List) -> Unit = {}, navigateToLocalNetworkSharingInfo: () -> Unit = {}, + navigateToDaitaInfo: () -> Unit = {}, + navigateToDaitaConfirmation: () -> Unit = {}, navigateToWireguardPortDialog: () -> Unit = {}, navigateToServerIpOverrides: () -> Unit = {}, onToggleBlockTrackers: (Boolean) -> Unit = {}, @@ -280,6 +295,7 @@ fun VpnSettingsScreen( onToggleBlockMalware: (Boolean) -> Unit = {}, onToggleAutoConnect: (Boolean) -> Unit = {}, onToggleLocalNetworkSharing: (Boolean) -> Unit = {}, + onDisableDaita: () -> Unit = {}, onToggleBlockAdultContent: (Boolean) -> Unit = {}, onToggleBlockGambling: (Boolean) -> Unit = {}, onToggleBlockSocialMedia: (Boolean) -> Unit = {}, @@ -497,8 +513,24 @@ fun VpnSettingsScreen( ) } - itemWithDivider { + item { + Spacer(modifier = Modifier.height(Dimens.cellLabelVerticalPadding)) + HeaderSwitchComposeCell( + title = stringResource(id = R.string.daita), + isToggled = state.isDaitaEnabled, + onCellClicked = { enable -> + if (enable) { + navigateToDaitaConfirmation() + } else { + onDisableDaita() + } + }, + onInfoClicked = navigateToDaitaInfo, + ) Spacer(modifier = Modifier.height(Dimens.cellLabelVerticalPadding)) + } + + itemWithDivider { InformationComposeCell( title = stringResource(id = R.string.wireguard_port_title), onInfoClicked = { navigateToWireguardPortInfo(state.availablePortRanges) }, diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/SelectLocationUiState.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/SelectLocationUiState.kt index 4fa2fbb1eb88..d8245792a330 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/SelectLocationUiState.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/SelectLocationUiState.kt @@ -22,6 +22,8 @@ sealed interface FilterChip { data class Ownership(val ownership: ModelOwnership) : FilterChip data class Provider(val count: Int) : FilterChip + + data object Daita : FilterChip } enum class RelayListItemContentType { diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/VpnSettingsUiState.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/VpnSettingsUiState.kt index 0eaf1695a7be..b57de21bc5b3 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/VpnSettingsUiState.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/VpnSettingsUiState.kt @@ -13,6 +13,7 @@ data class VpnSettingsUiState( val mtu: Mtu?, val isAutoConnectEnabled: Boolean, val isLocalNetworkSharingEnabled: Boolean, + val isDaitaEnabled: Boolean, val isCustomDnsEnabled: Boolean, val customDnsItems: List, val contentBlockersOptions: DefaultDnsOptions, @@ -31,6 +32,7 @@ data class VpnSettingsUiState( mtu: Mtu? = null, isAutoConnectEnabled: Boolean = false, isLocalNetworkSharingEnabled: Boolean = false, + isDaitaEnabled: Boolean = false, isCustomDnsEnabled: Boolean = false, customDnsItems: List = emptyList(), contentBlockersOptions: DefaultDnsOptions = DefaultDnsOptions(), @@ -46,6 +48,7 @@ data class VpnSettingsUiState( mtu, isAutoConnectEnabled, isLocalNetworkSharingEnabled, + isDaitaEnabled, isCustomDnsEnabled, customDnsItems, contentBlockersOptions, diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/di/UiModule.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/di/UiModule.kt index 1f1a48968420..6b909d394d40 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/di/UiModule.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/di/UiModule.kt @@ -134,10 +134,10 @@ val uiModule = module { single { CustomListActionUseCase(get(), get()) } single { SelectedLocationTitleUseCase(get(), get()) } single { AvailableProvidersUseCase(get()) } - single { FilterCustomListsRelayItemUseCase(get(), get()) } + single { FilterCustomListsRelayItemUseCase(get(), get(), get()) } single { CustomListsRelayItemUseCase(get(), get()) } single { CustomListRelayItemsUseCase(get(), get()) } - single { FilteredRelayListUseCase(get(), get()) } + single { FilteredRelayListUseCase(get(), get(), get()) } single { LastKnownLocationUseCase(get()) } single { InAppNotificationController(get(), get(), get(), get(), MainScope()) } @@ -184,7 +184,9 @@ val uiModule = module { viewModel { DnsDialogViewModel(get(), get(), get()) } viewModel { LoginViewModel(get(), get(), get()) } viewModel { PrivacyDisclaimerViewModel(get(), IS_PLAY_BUILD) } - viewModel { SelectLocationViewModel(get(), get(), get(), get(), get(), get(), get(), get()) } + viewModel { + SelectLocationViewModel(get(), get(), get(), get(), get(), get(), get(), get(), get()) + } viewModel { SettingsViewModel(get(), get(), IS_PLAY_BUILD) } viewModel { SplashViewModel(get(), get(), get(), get()) } viewModel { VoucherDialogViewModel(get()) } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayItemExtensions.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayItemExtensions.kt index c87a9548c50a..5d6e48a3f71b 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayItemExtensions.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayItemExtensions.kt @@ -53,27 +53,28 @@ private fun RelayItem.Location.hasProvider(providersConstraint: Constraint, providers: Constraint, + isDaitaEnabled: Boolean, ): RelayItem.CustomList { val newLocations = locations.mapNotNull { when (it) { - is RelayItem.Location.Country -> - it.filterOnOwnershipAndProvider(ownership, providers) - is RelayItem.Location.City -> it.filterOnOwnershipAndProvider(ownership, providers) - is RelayItem.Location.Relay -> it.filterOnOwnershipAndProvider(ownership, providers) + is RelayItem.Location.Country -> it.filter(ownership, providers, isDaitaEnabled) + is RelayItem.Location.City -> it.filter(ownership, providers, isDaitaEnabled) + is RelayItem.Location.Relay -> it.filter(ownership, providers, isDaitaEnabled) } } return copy(locations = newLocations) } -fun RelayItem.Location.Country.filterOnOwnershipAndProvider( +fun RelayItem.Location.Country.filter( ownership: Constraint, providers: Constraint, + isDaitaEnabled: Boolean, ): RelayItem.Location.Country? { - val cities = cities.mapNotNull { it.filterOnOwnershipAndProvider(ownership, providers) } + val cities = cities.mapNotNull { it.filter(ownership, providers, isDaitaEnabled) } return if (cities.isNotEmpty()) { this.copy(cities = cities) } else { @@ -81,11 +82,12 @@ fun RelayItem.Location.Country.filterOnOwnershipAndProvider( } } -private fun RelayItem.Location.City.filterOnOwnershipAndProvider( +private fun RelayItem.Location.City.filter( ownership: Constraint, providers: Constraint, + isDaitaEnabled: Boolean, ): RelayItem.Location.City? { - val relays = relays.mapNotNull { it.filterOnOwnershipAndProvider(ownership, providers) } + val relays = relays.mapNotNull { it.filter(ownership, providers, isDaitaEnabled) } return if (relays.isNotEmpty()) { this.copy(relays = relays) } else { @@ -93,11 +95,18 @@ private fun RelayItem.Location.City.filterOnOwnershipAndProvider( } } -private fun RelayItem.Location.Relay.filterOnOwnershipAndProvider( +private fun RelayItem.Location.Relay.hasMatchingDaitaSetting(isDaitaEnabled: Boolean): Boolean { + return if (isDaitaEnabled) daita else true +} + +private fun RelayItem.Location.Relay.filter( ownership: Constraint, providers: Constraint, + isDaitaEnabled: Boolean, ): RelayItem.Location.Relay? { - return if (hasOwnership(ownership) && hasProvider(providers)) { + return if ( + hasMatchingDaitaSetting(isDaitaEnabled) && hasOwnership(ownership) && hasProvider(providers) + ) { this } else { null 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 19e52b979074..d66d4a5c009c 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 @@ -68,4 +68,6 @@ class SettingsRepository( suspend fun setLocalNetworkSharing(isEnabled: Boolean) = managementService.setAllowLan(isEnabled) + + suspend fun setDaitaEnabled(enabled: Boolean) = managementService.setDaitaEnabled(enabled) } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/FilteredRelayListUseCase.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/FilteredRelayListUseCase.kt index 9c1994e075f0..60de94946f6f 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/FilteredRelayListUseCase.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/FilteredRelayListUseCase.kt @@ -5,25 +5,33 @@ import net.mullvad.mullvadvpn.lib.model.Constraint import net.mullvad.mullvadvpn.lib.model.Ownership import net.mullvad.mullvadvpn.lib.model.Providers import net.mullvad.mullvadvpn.lib.model.RelayItem -import net.mullvad.mullvadvpn.relaylist.filterOnOwnershipAndProvider +import net.mullvad.mullvadvpn.relaylist.filter import net.mullvad.mullvadvpn.repository.RelayListFilterRepository import net.mullvad.mullvadvpn.repository.RelayListRepository +import net.mullvad.mullvadvpn.repository.SettingsRepository class FilteredRelayListUseCase( private val relayListRepository: RelayListRepository, private val relayListFilterRepository: RelayListFilterRepository, + private val settingsRepository: SettingsRepository, ) { operator fun invoke() = combine( relayListRepository.relayList, relayListFilterRepository.selectedOwnership, relayListFilterRepository.selectedProviders, - ) { relayList, selectedOwnership, selectedProviders -> - relayList.filterOnOwnershipAndProvider(selectedOwnership, selectedProviders) + settingsRepository.settingsUpdates, + ) { relayList, selectedOwnership, selectedProviders, settings -> + relayList.filter( + selectedOwnership, + selectedProviders, + isDaitaEnabled = settings?.isDaitaEnabled() ?: false, + ) } - private fun List.filterOnOwnershipAndProvider( + private fun List.filter( ownership: Constraint, providers: Constraint, - ) = mapNotNull { it.filterOnOwnershipAndProvider(ownership, providers) } + isDaitaEnabled: Boolean, + ) = mapNotNull { it.filter(ownership, providers, isDaitaEnabled) } } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/customlists/FilterCustomListsRelayItemUseCase.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/customlists/FilterCustomListsRelayItemUseCase.kt index 49c8ec89f6c1..17ead75d2ac9 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/customlists/FilterCustomListsRelayItemUseCase.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/customlists/FilterCustomListsRelayItemUseCase.kt @@ -6,12 +6,14 @@ import net.mullvad.mullvadvpn.lib.model.Constraint import net.mullvad.mullvadvpn.lib.model.Ownership import net.mullvad.mullvadvpn.lib.model.Providers import net.mullvad.mullvadvpn.lib.model.RelayItem -import net.mullvad.mullvadvpn.relaylist.filterOnOwnershipAndProvider +import net.mullvad.mullvadvpn.relaylist.filter import net.mullvad.mullvadvpn.repository.RelayListFilterRepository +import net.mullvad.mullvadvpn.repository.SettingsRepository class FilterCustomListsRelayItemUseCase( private val customListsRelayItemUseCase: CustomListsRelayItemUseCase, private val relayListFilterRepository: RelayListFilterRepository, + private val settingsRepository: SettingsRepository, ) { operator fun invoke() = @@ -19,12 +21,18 @@ class FilterCustomListsRelayItemUseCase( customListsRelayItemUseCase(), relayListFilterRepository.selectedOwnership, relayListFilterRepository.selectedProviders, - ) { customLists, selectedOwnership, selectedProviders -> - customLists.filterOnOwnershipAndProvider(selectedOwnership, selectedProviders) + settingsRepository.settingsUpdates, + ) { customLists, selectedOwnership, selectedProviders, settings -> + customLists.filterOnOwnershipAndProvider( + selectedOwnership, + selectedProviders, + isDaitaEnabled = settings?.isDaitaEnabled() ?: false, + ) } private fun List.filterOnOwnershipAndProvider( ownership: Constraint, providers: Constraint, - ) = mapNotNull { it.filterOnOwnershipAndProvider(ownership, providers) } + isDaitaEnabled: Boolean, + ) = mapNotNull { it.filter(ownership, providers, isDaitaEnabled = isDaitaEnabled) } } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/SelectLocationViewModel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/SelectLocationViewModel.kt index 04476ea560db..c34b182aa673 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/SelectLocationViewModel.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/SelectLocationViewModel.kt @@ -36,6 +36,7 @@ import net.mullvad.mullvadvpn.relaylist.newFilterOnSearch import net.mullvad.mullvadvpn.repository.CustomListsRepository import net.mullvad.mullvadvpn.repository.RelayListFilterRepository import net.mullvad.mullvadvpn.repository.RelayListRepository +import net.mullvad.mullvadvpn.repository.SettingsRepository import net.mullvad.mullvadvpn.usecase.AvailableProvidersUseCase import net.mullvad.mullvadvpn.usecase.FilteredRelayListUseCase import net.mullvad.mullvadvpn.usecase.customlists.CustomListActionUseCase @@ -51,6 +52,7 @@ class SelectLocationViewModel( private val customListActionUseCase: CustomListActionUseCase, private val filteredRelayListUseCase: FilteredRelayListUseCase, private val relayListRepository: RelayListRepository, + private val settingsRepository: SettingsRepository, ) : ViewModel() { private val _searchTerm = MutableStateFlow(EMPTY_SEARCH_TERM) @@ -110,7 +112,8 @@ class SelectLocationViewModel( relayListFilterRepository.selectedOwnership, relayListFilterRepository.selectedProviders, availableProvidersUseCase(), - ) { selectedOwnership, selectedConstraintProviders, allProviders -> + settingsRepository.settingsUpdates, + ) { selectedOwnership, selectedConstraintProviders, allProviders, settings -> val ownershipFilter = selectedOwnership.getOrNull() val providerCountFilter = when (selectedConstraintProviders) { @@ -122,7 +125,6 @@ class SelectLocationViewModel( ) .size } - buildList { if (ownershipFilter != null) { add(FilterChip.Ownership(ownershipFilter)) @@ -130,6 +132,9 @@ class SelectLocationViewModel( if (providerCountFilter != null) { add(FilterChip.Provider(providerCountFilter)) } + if (settings?.isDaitaEnabled() == true) { + add(FilterChip.Daita) + } } } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/VpnSettingsViewModel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/VpnSettingsViewModel.kt index abaad265fe12..af2ea72e4e79 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/VpnSettingsViewModel.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/VpnSettingsViewModel.kt @@ -63,6 +63,7 @@ class VpnSettingsViewModel( mtuValue = settings?.tunnelOptions?.wireguard?.mtu, isAutoConnectEnabled = settings?.autoConnect ?: false, isLocalNetworkSharingEnabled = settings?.allowLan ?: false, + isDaitaEnabled = settings?.isDaitaEnabled() ?: false, isCustomDnsEnabled = settings?.isCustomDnsEnabled() ?: false, customDnsList = settings?.addresses()?.asStringAddressList() ?: listOf(), contentBlockersOptions = @@ -123,6 +124,14 @@ class VpnSettingsViewModel( } } + fun onToggleDaita(enable: Boolean) { + viewModelScope.launch(dispatcher) { + repository.setDaitaEnabled(enable).onLeft { + _uiSideEffect.send(VpnSettingsSideEffect.ShowToast.GenericError) + } + } + } + fun onDnsDialogDismissed() { if (vmState.value.customDnsList.isEmpty()) { onToggleCustomDns(enable = false) diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/VpnSettingsViewModelState.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/VpnSettingsViewModelState.kt index 534263b44b1f..676a10cd70bf 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/VpnSettingsViewModelState.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/VpnSettingsViewModelState.kt @@ -13,6 +13,7 @@ data class VpnSettingsViewModelState( val mtuValue: Mtu?, val isAutoConnectEnabled: Boolean, val isLocalNetworkSharingEnabled: Boolean, + val isDaitaEnabled: Boolean, val isCustomDnsEnabled: Boolean, val customDnsList: List, val contentBlockersOptions: DefaultDnsOptions, @@ -29,6 +30,7 @@ data class VpnSettingsViewModelState( mtuValue, isAutoConnectEnabled, isLocalNetworkSharingEnabled, + isDaitaEnabled, isCustomDnsEnabled, customDnsList, contentBlockersOptions, @@ -47,6 +49,7 @@ data class VpnSettingsViewModelState( mtuValue = null, isAutoConnectEnabled = false, isLocalNetworkSharingEnabled = false, + isDaitaEnabled = false, isCustomDnsEnabled = false, customDnsList = listOf(), contentBlockersOptions = DefaultDnsOptions(), diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/CustomListLocationsViewModelTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/CustomListLocationsViewModelTest.kt index e4012abd9e0e..1e0de9c96e15 100644 --- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/CustomListLocationsViewModelTest.kt +++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/CustomListLocationsViewModelTest.kt @@ -322,6 +322,7 @@ class CustomListLocationsViewModelTest { ProviderId("Provider"), ownership = Ownership.MullvadOwned, ), + daita = false, ) ), ) diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/SelectLocationViewModelTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/SelectLocationViewModelTest.kt index 5a44f6db06a7..bee888d279c3 100644 --- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/SelectLocationViewModelTest.kt +++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/SelectLocationViewModelTest.kt @@ -32,10 +32,12 @@ import net.mullvad.mullvadvpn.lib.model.Provider import net.mullvad.mullvadvpn.lib.model.Providers import net.mullvad.mullvadvpn.lib.model.RelayItem import net.mullvad.mullvadvpn.lib.model.RelayItemId +import net.mullvad.mullvadvpn.lib.model.Settings import net.mullvad.mullvadvpn.relaylist.descendants import net.mullvad.mullvadvpn.repository.CustomListsRepository import net.mullvad.mullvadvpn.repository.RelayListFilterRepository import net.mullvad.mullvadvpn.repository.RelayListRepository +import net.mullvad.mullvadvpn.repository.SettingsRepository import net.mullvad.mullvadvpn.usecase.AvailableProvidersUseCase import net.mullvad.mullvadvpn.usecase.FilteredRelayListUseCase import net.mullvad.mullvadvpn.usecase.customlists.CustomListActionUseCase @@ -58,6 +60,9 @@ class SelectLocationViewModelTest { private val mockCustomListsRepository: CustomListsRepository = mockk() private val mockCustomListsRelayItemUseCase: CustomListsRelayItemUseCase = mockk() + private val mockSettingsRepository: SettingsRepository = mockk() + private val settingsFlow = MutableStateFlow(mockk(relaxed = true)) + private lateinit var viewModel: SelectLocationViewModel private val allProviders = MutableStateFlow>(emptyList()) @@ -79,6 +84,7 @@ class SelectLocationViewModelTest { every { mockFilteredRelayListUseCase() } returns filteredRelayList every { mockFilteredCustomListRelayItemsUseCase() } returns filteredCustomRelayListItems every { mockCustomListsRelayItemUseCase() } returns customListsRelayItem + every { mockSettingsRepository.settingsUpdates } returns settingsFlow mockkStatic(RELAY_LIST_EXTENSIONS) mockkStatic(RELAY_ITEM_EXTENSIONS) @@ -93,6 +99,7 @@ class SelectLocationViewModelTest { relayListRepository = mockRelayListRepository, customListsRepository = mockCustomListsRepository, customListsRelayItemUseCase = mockCustomListsRelayItemUseCase, + settingsRepository = mockSettingsRepository, ) } diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/VpnSettingsViewModelTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/VpnSettingsViewModelTest.kt index c2f9ca34a68e..89456c1d028a 100644 --- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/VpnSettingsViewModelTest.kt +++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/VpnSettingsViewModelTest.kt @@ -113,7 +113,11 @@ class VpnSettingsViewModelTest { val mockTunnelOptions: TunnelOptions = mockk(relaxed = true) // Can not use a mock here since mocking a value class val leads to class cast exception val mockWireguardTunnelOptions = - WireguardTunnelOptions(mtu = Mtu(0), quantumResistant = expectedResistantState) + WireguardTunnelOptions( + mtu = Mtu(0), + quantumResistant = expectedResistantState, + daita = false, + ) every { mockSettings.tunnelOptions } returns mockTunnelOptions every { mockTunnelOptions.wireguard } returns mockWireguardTunnelOptions @@ -146,6 +150,7 @@ class VpnSettingsViewModelTest { WireguardTunnelOptions( mtu = null, quantumResistant = QuantumResistantState.Off, + daita = false, ), dnsOptions = mockk(relaxed = true), ) diff --git a/android/lib/daemon-grpc/src/main/kotlin/net/mullvad/mullvadvpn/lib/daemon/grpc/ManagementService.kt b/android/lib/daemon-grpc/src/main/kotlin/net/mullvad/mullvadvpn/lib/daemon/grpc/ManagementService.kt index b51fd7eb2ff0..f444edce39f3 100644 --- a/android/lib/daemon-grpc/src/main/kotlin/net/mullvad/mullvadvpn/lib/daemon/grpc/ManagementService.kt +++ b/android/lib/daemon-grpc/src/main/kotlin/net/mullvad/mullvadvpn/lib/daemon/grpc/ManagementService.kt @@ -104,6 +104,7 @@ import net.mullvad.mullvadvpn.lib.model.SelectedObfuscation import net.mullvad.mullvadvpn.lib.model.SetAllowLanError import net.mullvad.mullvadvpn.lib.model.SetApiAccessMethodError import net.mullvad.mullvadvpn.lib.model.SetAutoConnectError +import net.mullvad.mullvadvpn.lib.model.SetDaitaSettingsError import net.mullvad.mullvadvpn.lib.model.SetDnsOptionsError import net.mullvad.mullvadvpn.lib.model.SetObfuscationOptionsError import net.mullvad.mullvadvpn.lib.model.SetRelayLocationError @@ -501,6 +502,15 @@ class ManagementService( .mapLeft(SetAllowLanError::Unknown) .mapEmpty() + suspend fun setDaitaEnabled(enabled: Boolean): Either = + Either.catch { + val daitaSettings = + ManagementInterface.DaitaSettings.newBuilder().setEnabled(enabled).build() + grpc.setDaitaSettings(daitaSettings) + } + .mapLeft(SetDaitaSettingsError::Unknown) + .mapEmpty() + suspend fun setRelayLocation(location: ModelRelayItemId): Either = Either.catch { val currentRelaySettings = getSettings().relaySettings 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 5e8df1edc4b0..1ff297312bec 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 @@ -68,6 +68,7 @@ import net.mullvad.mullvadvpn.lib.model.TunnelState import net.mullvad.mullvadvpn.lib.model.Udp2TcpObfuscationSettings import net.mullvad.mullvadvpn.lib.model.WireguardConstraints import net.mullvad.mullvadvpn.lib.model.WireguardEndpointData +import net.mullvad.mullvadvpn.lib.model.WireguardRelayEndpointData import net.mullvad.mullvadvpn.lib.model.WireguardTunnelOptions import org.joda.time.Instant @@ -158,6 +159,7 @@ internal fun ManagementInterface.TunnelEndpoint.toDomain(): TunnelEndpoint = } else { null }, + daita = daita, ) internal fun ManagementInterface.ObfuscationEndpoint.toDomain(): ObfuscationEndpoint = @@ -370,6 +372,7 @@ internal fun ManagementInterface.TunnelOptions.WireguardOptions.toDomain(): Wire WireguardTunnelOptions( mtu = if (hasMtu()) Mtu(mtu) else null, quantumResistant = quantumResistant.toDomain(), + daita = daita.enabled, ) internal fun ManagementInterface.QuantumResistantState.toDomain(): QuantumResistantState = @@ -442,6 +445,9 @@ internal fun ManagementInterface.RelayList.toDomain(): RelayList = internal fun ManagementInterface.WireguardEndpointData.toDomain(): WireguardEndpointData = WireguardEndpointData(portRangesList.map { it.toDomain() }) +internal fun ManagementInterface.WireguardRelayEndpointData.toDomain(): WireguardRelayEndpointData = + WireguardRelayEndpointData(daita) + internal fun ManagementInterface.PortRange.toDomain(): PortRange = PortRange(first..last) /** @@ -493,6 +499,12 @@ internal fun ManagementInterface.Relay.toDomain( ProviderId(provider), ownership = if (owned) Ownership.MullvadOwned else Ownership.Rented, ), + daita = + if ( + hasEndpointData() && endpointType == ManagementInterface.Relay.RelayType.WIREGUARD + ) { + ManagementInterface.WireguardRelayEndpointData.parseFrom(endpointData.value).daita + } else false, ) internal fun ManagementInterface.Device.toDomain(): Device = @@ -596,11 +608,11 @@ internal fun ManagementInterface.FeatureIndicator.toDomain() = ManagementInterface.FeatureIndicator.SERVER_IP_OVERRIDE -> FeatureIndicator.SERVER_IP_OVERRIDE ManagementInterface.FeatureIndicator.CUSTOM_MTU -> FeatureIndicator.CUSTOM_MTU + ManagementInterface.FeatureIndicator.DAITA -> FeatureIndicator.DAITA 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/daemon-grpc/src/test/kotlin/net/mullvad/mullvadvpn/lib/daemon/grpc/RelayNameComparatorTest.kt b/android/lib/daemon-grpc/src/test/kotlin/net/mullvad/mullvadvpn/lib/daemon/grpc/RelayNameComparatorTest.kt index 1494851bcb69..e496c6d5aa74 100644 --- a/android/lib/daemon-grpc/src/test/kotlin/net/mullvad/mullvadvpn/lib/daemon/grpc/RelayNameComparatorTest.kt +++ b/android/lib/daemon-grpc/src/test/kotlin/net/mullvad/mullvadvpn/lib/daemon/grpc/RelayNameComparatorTest.kt @@ -29,6 +29,7 @@ class RelayNameComparatorTest { providerId = ProviderId("Provider"), ownership = Ownership.MullvadOwned, ), + daita = false, ) val relay10 = RelayItem.Location.Relay( @@ -39,6 +40,7 @@ class RelayNameComparatorTest { providerId = ProviderId("Provider"), ownership = Ownership.MullvadOwned, ), + daita = false, ) relay9 assertOrderBothDirection relay10 @@ -55,6 +57,7 @@ class RelayNameComparatorTest { providerId = ProviderId("Provider"), ownership = Ownership.MullvadOwned, ), + daita = false, ) val relay9b = RelayItem.Location.Relay( @@ -65,6 +68,7 @@ class RelayNameComparatorTest { providerId = ProviderId("Provider"), ownership = Ownership.MullvadOwned, ), + daita = false, ) assertTrue(RelayNameComparator.compare(relay9a, relay9b) == 0) @@ -82,6 +86,7 @@ class RelayNameComparatorTest { providerId = ProviderId("Provider"), ownership = Ownership.MullvadOwned, ), + daita = false, ) val relay1 = RelayItem.Location.Relay( @@ -92,6 +97,7 @@ class RelayNameComparatorTest { providerId = ProviderId("Provider"), ownership = Ownership.MullvadOwned, ), + daita = false, ) val relay3 = RelayItem.Location.Relay( @@ -102,6 +108,7 @@ class RelayNameComparatorTest { providerId = ProviderId("Provider"), ownership = Ownership.MullvadOwned, ), + daita = false, ) val relay100 = RelayItem.Location.Relay( @@ -112,6 +119,7 @@ class RelayNameComparatorTest { providerId = ProviderId("Provider"), ownership = Ownership.MullvadOwned, ), + daita = false, ) relay001 assertOrderBothDirection relay1 @@ -131,6 +139,7 @@ class RelayNameComparatorTest { providerId = ProviderId("Provider"), ownership = Ownership.MullvadOwned, ), + daita = false, ) val relay9b = RelayItem.Location.Relay( @@ -141,6 +150,7 @@ class RelayNameComparatorTest { providerId = ProviderId("Provider"), ownership = Ownership.MullvadOwned, ), + daita = false, ) assertTrue(RelayNameComparator.compare(relay9a, relay9b) == 0) @@ -158,6 +168,7 @@ class RelayNameComparatorTest { providerId = ProviderId("Provider"), ownership = Ownership.MullvadOwned, ), + daita = false, ) val relay005 = RelayItem.Location.Relay( @@ -168,6 +179,7 @@ class RelayNameComparatorTest { providerId = ProviderId("Provider"), ownership = Ownership.MullvadOwned, ), + daita = false, ) relay001 assertOrderBothDirection relay005 @@ -184,6 +196,7 @@ class RelayNameComparatorTest { providerId = ProviderId("Provider"), ownership = Ownership.MullvadOwned, ), + daita = false, ) val relayAr8 = RelayItem.Location.Relay( @@ -194,6 +207,7 @@ class RelayNameComparatorTest { providerId = ProviderId("Provider"), ownership = Ownership.MullvadOwned, ), + daita = false, ) val relaySe5 = RelayItem.Location.Relay( @@ -204,6 +218,7 @@ class RelayNameComparatorTest { providerId = ProviderId("Provider"), ownership = Ownership.MullvadOwned, ), + daita = false, ) val relaySe10 = RelayItem.Location.Relay( @@ -214,6 +229,7 @@ class RelayNameComparatorTest { providerId = ProviderId("Provider"), ownership = Ownership.MullvadOwned, ), + daita = false, ) relayAr2 assertOrderBothDirection relayAr8 @@ -232,6 +248,7 @@ class RelayNameComparatorTest { providerId = ProviderId("Provider"), ownership = Ownership.MullvadOwned, ), + daita = false, ) val relay2w = RelayItem.Location.Relay( @@ -242,6 +259,7 @@ class RelayNameComparatorTest { providerId = ProviderId("Provider"), ownership = Ownership.MullvadOwned, ), + daita = false, ) relay2c assertOrderBothDirection relay2w @@ -258,6 +276,7 @@ class RelayNameComparatorTest { providerId = ProviderId("Provider"), ownership = Ownership.MullvadOwned, ), + daita = false, ) val relay22b = RelayItem.Location.Relay( @@ -268,6 +287,7 @@ class RelayNameComparatorTest { providerId = ProviderId("Provider"), ownership = Ownership.MullvadOwned, ), + daita = false, ) relay22a assertOrderBothDirection relay22b 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 index 7ad0b3ab6998..d11f40586950 100644 --- 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 @@ -9,12 +9,12 @@ enum class FeatureIndicator { CUSTOM_DNS, SERVER_IP_OVERRIDE, CUSTOM_MTU, + DAITA, // 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/RelayItem.kt b/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/RelayItem.kt index edf00d4cb4fa..af96c4d94d76 100644 --- a/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/RelayItem.kt +++ b/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/RelayItem.kt @@ -58,6 +58,7 @@ sealed interface RelayItem { override val id: GeoLocationId.Hostname, val provider: Provider, override val active: Boolean, + val daita: Boolean, ) : Location { override val name: String = id.code override val hasChildren: Boolean = false diff --git a/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/SetDaitaSettingsError.kt b/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/SetDaitaSettingsError.kt new file mode 100644 index 000000000000..f636267c0914 --- /dev/null +++ b/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/SetDaitaSettingsError.kt @@ -0,0 +1,5 @@ +package net.mullvad.mullvadvpn.lib.model + +sealed interface SetDaitaSettingsError { + data class Unknown(val throwable: Throwable) : SetDaitaSettingsError +} diff --git a/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/Settings.kt b/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/Settings.kt index 0c15202beeca..f59d85184cfd 100644 --- a/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/Settings.kt +++ b/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/Settings.kt @@ -15,5 +15,7 @@ data class Settings( val splitTunnelSettings: SplitTunnelSettings, val apiAccessMethodSettings: List, ) { + fun isDaitaEnabled() = tunnelOptions.wireguard.daita + companion object } diff --git a/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/TunnelEndpoint.kt b/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/TunnelEndpoint.kt index 1465997cc57c..3902e5c96596 100644 --- a/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/TunnelEndpoint.kt +++ b/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/TunnelEndpoint.kt @@ -4,4 +4,5 @@ data class TunnelEndpoint( val endpoint: Endpoint, val quantumResistant: Boolean, val obfuscation: ObfuscationEndpoint?, + val daita: Boolean, ) 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 53d468c01ec4..1849c5abf90a 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 @@ -38,4 +38,12 @@ sealed class TunnelState { is Error -> this.errorState.isBlocking } } + + fun isUsingDaita(): Boolean { + return when (this) { + is Connected -> endpoint.daita + is Connecting -> endpoint?.daita ?: false + else -> false + } + } } diff --git a/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/WireguardRelayEndpointData.kt b/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/WireguardRelayEndpointData.kt new file mode 100644 index 000000000000..9e328b92e636 --- /dev/null +++ b/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/WireguardRelayEndpointData.kt @@ -0,0 +1,3 @@ +package net.mullvad.mullvadvpn.lib.model + +data class WireguardRelayEndpointData(val daita: Boolean) diff --git a/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/WireguardTunnelOptions.kt b/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/WireguardTunnelOptions.kt index 573f08213ef0..70b1599c5557 100644 --- a/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/WireguardTunnelOptions.kt +++ b/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/WireguardTunnelOptions.kt @@ -1,3 +1,7 @@ package net.mullvad.mullvadvpn.lib.model -data class WireguardTunnelOptions(val mtu: Mtu?, val quantumResistant: QuantumResistantState) +data class WireguardTunnelOptions( + val mtu: Mtu?, + val quantumResistant: QuantumResistantState, + val daita: Boolean, +) diff --git a/android/lib/resource/src/main/res/values/strings.xml b/android/lib/resource/src/main/res/values/strings.xml index 071f45ae2305..b72902897f00 100644 --- a/android/lib/resource/src/main/res/values/strings.xml +++ b/android/lib/resource/src/main/res/values/strings.xml @@ -385,4 +385,10 @@ Failed to set to current - Unknown reason %s was removed from \"%s\" \"%s\" was created + %s (%s) hides patterns in your encrypted VPN traffic. If anyone is monitoring your connection, this makes it significantly harder for them to identify what websites you are visiting. It does this by carefully adding network noise and making all network packets the same size. + Attention: Since this increases your total network traffic, be cautious if you have a limited data plan. It can also negatively impact your network speed. Please consider this if you want to enable %s. + %s using %s + Setting: %s + Enable anyway + This feature isn’t available on all servers. You might need to change location after enabling. diff --git a/android/lib/resource/src/main/res/values/strings_non_translatable.xml b/android/lib/resource/src/main/res/values/strings_non_translatable.xml index 110e112e99f3..9cf571171a8b 100644 --- a/android/lib/resource/src/main/res/values/strings_non_translatable.xml +++ b/android/lib/resource/src/main/res/values/strings_non_translatable.xml @@ -14,4 +14,6 @@
  • 10.0.0.0/8
  • 172.16.0.0/12
  • 192.168.0.0/16
  • 169.254.0.0/16
  • fe80::/10
  • fc00::/7
  • ]]>
    + DAITA + Defence against AI-guided Traffic Analysis diff --git a/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/FileResourceExtractor.kt b/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/FileResourceExtractor.kt deleted file mode 100644 index 71a05e674385..000000000000 --- a/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/FileResourceExtractor.kt +++ /dev/null @@ -1,23 +0,0 @@ -package net.mullvad.mullvadvpn.service - -import android.content.Context -import java.io.File -import java.io.FileOutputStream - -class FileResourceExtractor(val context: Context) { - fun extract(asset: String, force: Boolean = false) { - val destination = File(context.filesDir, asset) - - if (!destination.exists() || force) { - extractFile(asset, destination) - } - } - - private fun extractFile(asset: String, destination: File) { - val destinationStream = FileOutputStream(destination) - - context.assets.open(asset).copyTo(destinationStream) - - destinationStream.close() - } -} diff --git a/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadVpnService.kt b/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadVpnService.kt index 2db5a00fdbc0..ebdcbec78019 100644 --- a/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadVpnService.kt +++ b/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadVpnService.kt @@ -10,7 +10,6 @@ import androidx.core.content.getSystemService import androidx.lifecycle.lifecycleScope import arrow.atomic.AtomicInt import co.touchlab.kermit.Logger -import java.io.File import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.filterIsInstance import kotlinx.coroutines.launch @@ -27,11 +26,13 @@ import net.mullvad.mullvadvpn.service.migration.MigrateSplitTunneling import net.mullvad.mullvadvpn.service.notifications.ForegroundNotificationManager import net.mullvad.mullvadvpn.service.notifications.NotificationChannelFactory import net.mullvad.mullvadvpn.service.notifications.NotificationManager +import net.mullvad.mullvadvpn.service.util.extractAndOverwriteIfAssetMoreRecent import net.mullvad.talpid.TalpidVpnService import org.koin.android.ext.android.getKoin import org.koin.core.context.loadKoinModules -private const val RELAYS_FILE = "relays.json" +private const val RELAY_LIST_ASSET_NAME = "relays.json" +private const val MAYBENOT_MACHINES_ASSET_NAME = "maybenot_machines" class MullvadVpnService : TalpidVpnService() { @@ -72,7 +73,7 @@ class MullvadVpnService : TalpidVpnService() { keyguardManager = getSystemService()!! - prepareFiles(this@MullvadVpnService) + prepareFiles() migrateSplitTunneling.migrate() // If it is a debug build and we have an api override in the intent, use it @@ -221,16 +222,11 @@ class MullvadVpnService : TalpidVpnService() { return this?.action == SERVICE_INTERFACE } - private fun prepareFiles(context: Context) { - val shouldOverwriteRelayList = - lastUpdatedTime(context) > File(context.filesDir, RELAYS_FILE).lastModified() - - FileResourceExtractor(context).apply { extract(RELAYS_FILE, shouldOverwriteRelayList) } + private fun Context.prepareFiles() { + extractAndOverwriteIfAssetMoreRecent(RELAY_LIST_ASSET_NAME) + extractAndOverwriteIfAssetMoreRecent(MAYBENOT_MACHINES_ASSET_NAME) } - private fun lastUpdatedTime(context: Context): Long = - context.packageManager.getPackageInfo(context.packageName, 0).lastUpdateTime - companion object { init { System.loadLibrary("mullvad_jni") diff --git a/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/util/ContextExtensions.kt b/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/util/ContextExtensions.kt new file mode 100644 index 000000000000..51240fa16d62 --- /dev/null +++ b/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/util/ContextExtensions.kt @@ -0,0 +1,23 @@ +package net.mullvad.mullvadvpn.service.util + +import android.content.Context +import java.io.File +import java.io.FileOutputStream + +fun Context.extractAndOverwriteIfAssetMoreRecent(assetName: String) { + val forceOverwriteIfMoreRecent = lastUpdatedTime() > File(filesDir, assetName).lastModified() + val destination = File(filesDir, assetName) + + if (!destination.exists() || forceOverwriteIfMoreRecent) { + extractFile(assetName, destination) + } +} + +private fun Context.lastUpdatedTime(): Long = + packageManager.getPackageInfo(packageName, 0).lastUpdateTime + +private fun Context.extractFile(asset: String, destination: File) { + val destinationStream = FileOutputStream(destination) + assets.open(asset).copyTo(destinationStream) + destinationStream.close() +} diff --git a/build-apk.sh b/build-apk.sh index 190e4e1f2ab2..e45d4014c9d4 100755 --- a/build-apk.sh +++ b/build-apk.sh @@ -114,6 +114,9 @@ done echo "Updating relays.json..." cargo run --bin relay_list "${CARGO_ARGS[@]}" > android/app/build/extraAssets/relays.json +echo "Copying maybenot machines..." +cp dist-assets/maybenot_machines android/app/build/extraAssets/maybenot_machines + cd "$SCRIPT_DIR/android" $GRADLE_CMD --console plain "${GRADLE_TASKS[@]}" From 5367c566e3e1928f423b02abd99ea1368348c48a Mon Sep 17 00:00:00 2001 From: Albin Date: Tue, 27 Aug 2024 14:42:12 +0200 Subject: [PATCH 5/6] Update translations --- android/lib/resource/src/main/res/values-da/strings.xml | 5 +++++ android/lib/resource/src/main/res/values-de/strings.xml | 5 +++++ android/lib/resource/src/main/res/values-es/strings.xml | 5 +++++ android/lib/resource/src/main/res/values-fi/strings.xml | 5 +++++ android/lib/resource/src/main/res/values-fr/strings.xml | 5 +++++ android/lib/resource/src/main/res/values-it/strings.xml | 5 +++++ android/lib/resource/src/main/res/values-ja/strings.xml | 5 +++++ android/lib/resource/src/main/res/values-ko/strings.xml | 5 +++++ android/lib/resource/src/main/res/values-my/strings.xml | 5 +++++ android/lib/resource/src/main/res/values-nb/strings.xml | 5 +++++ android/lib/resource/src/main/res/values-nl/strings.xml | 5 +++++ android/lib/resource/src/main/res/values-pl/strings.xml | 5 +++++ android/lib/resource/src/main/res/values-pt/strings.xml | 5 +++++ android/lib/resource/src/main/res/values-ru/strings.xml | 5 +++++ android/lib/resource/src/main/res/values-sv/strings.xml | 5 +++++ android/lib/resource/src/main/res/values-th/strings.xml | 5 +++++ android/lib/resource/src/main/res/values-tr/strings.xml | 5 +++++ android/lib/resource/src/main/res/values-zh-rCN/strings.xml | 5 +++++ android/lib/resource/src/main/res/values-zh-rTW/strings.xml | 5 +++++ gui/locales/messages.pot | 3 +++ 20 files changed, 98 insertions(+) diff --git a/android/lib/resource/src/main/res/values-da/strings.xml b/android/lib/resource/src/main/res/values-da/strings.xml index dd8beebc9b4f..8ac9978676ca 100644 --- a/android/lib/resource/src/main/res/values-da/strings.xml +++ b/android/lib/resource/src/main/res/values-da/strings.xml @@ -74,6 +74,7 @@ Ja, log enhed af Tillykke! Gør min forbindelse sikker + %1$s via %2$s Tilslutter Opretter forbindelse til Mullvad-systemtjeneste... Fortsæt med login @@ -100,6 +101,8 @@ Brugerdefineret WireGuard-port Gyldige områder: %1$s Kunne ikke fortolke værten for den tilpassede tunnel. Prøv at ændre dine indstillinger. + %1$s (%2$s) skjuler mønstre i din krypterede VPN-trafik. Hvis nogen overvåger din forbindelse, gør dette det betydeligt sværere for dem at identificere, hvilke websteder du besøger. Mønstrene skjules ved omhyggeligt at tilføje netværksstøj og gøre alle netværkspakker lige store. + Bemærk: Da dette øger din samlede netværkstrafik, skal du være forsigtig, hvis du har et abonnement med begrænset datamængde. Det kan også påvirke din netværkshastighed negativt. Overvej dette, hvis du vil aktivere %1$s. Slet Vil du slette \"%1$s\"? \"%1$s\" blev slettet @@ -133,6 +136,7 @@ Rediger metode Rediger navn Aktiver + Aktivér alligevel Brug brugerdefineret DNS-server Aktiver metode Indtast MTU @@ -288,6 +292,7 @@ Nulstil alle tilsidesættelser Kan ikke indstille systemets DNS-server. Indsend en problemrapport. Kan ikke anvende firewallregler. Fejlfind eller send en problemrapport. + Indstilling: %1$s Indstillinger Konto API-adgang diff --git a/android/lib/resource/src/main/res/values-de/strings.xml b/android/lib/resource/src/main/res/values-de/strings.xml index 70f3c4a5bb47..ca1ae2cd27b0 100644 --- a/android/lib/resource/src/main/res/values-de/strings.xml +++ b/android/lib/resource/src/main/res/values-de/strings.xml @@ -74,6 +74,7 @@ Ja, von Gerät abmelden Glückwunsch! Meine Verbindung sichern + %1$s verwendet %2$s Verbinden Verbindung zum Mullvad-Systemdienst wird hergestellt... Weiter mit Anmeldung @@ -100,6 +101,8 @@ Eigener WireGuard-Port Gültige Bereiche: %1$s Der Host des benutzerdefinierten Tunnels konnte nicht aufgelöst werden. Versuchen Sie, Ihre Einstellungen zu ändern. + %1$s (%2$s) verbirgt Muster in Ihrem verschlüsselten VPN-Traffic. Wenn jemand Ihre Verbindung überwacht, ist es für ihn wesentlich schwieriger zu erkennen, welche Websites Sie besuchen. Dazu fügt es vorsichtig Netzwerkrauschen hinzu und sorgt dafür, dass alle Netzwerkpakete die gleiche Größe haben. + Achtung! Da dies Ihren gesamten Netzwerktraffic erhöht, sollten Sie vorsichtig sein, wenn Sie einen begrenzten Datentarif haben. Außerdem kann sich dies negativ auf Ihre Netzwerkgeschwindigkeit auswirken. Bitte beachten Sie dies, wenn Sie %1$s aktivieren möchten. Löschen „%1$s“ löschen? „%1$s“ wurde gelöscht @@ -133,6 +136,7 @@ Methode bearbeiten Name bearbeiten Aktivieren + Trotzdem aktivieren Benutzerdefinierten DNS-Server verwenden Methode aktivieren MTU eingeben @@ -288,6 +292,7 @@ Alle Überschreibungen zurücksetzen Der DNS-Server des Systems konnte nicht eingestellt werden. Bitte senden Sie einen Problembericht. Firewall-Regeln können nicht angewendet werden. Bitte beheben Sie das Problem oder senden Sie einen Problembericht. + Einstellung: %1$s Einstellungen Konto API-Zugriff diff --git a/android/lib/resource/src/main/res/values-es/strings.xml b/android/lib/resource/src/main/res/values-es/strings.xml index 876d5afcddc5..89272f7603ad 100644 --- a/android/lib/resource/src/main/res/values-es/strings.xml +++ b/android/lib/resource/src/main/res/values-es/strings.xml @@ -74,6 +74,7 @@ Sí, cerrar sesión ¡Enhorabuena! Proteger mi conexión + %1$s utilizando %2$s Conectando Conectando al servicio del sistema Mullvad… Iniciar sesión @@ -100,6 +101,8 @@ Puerto personalizado de WireGuard Intervalos válidos: %1$s No se puede resolver el host del túnel personalizado. Pruebe a cambiar la configuración. + %1$s (%2$s) oculta los patrones en su tráfico VPN cifrado. Si alguien supervisa su conexión, esto les dificulta notablemente identificar qué sitios web está visitando. Lo realiza añadiendo con cuidado ruido de red y haciendo que todos los paquetes de red tengan el mismo tamaño. + Atención: Como esto aumenta su tráfico de red total, tenga cuidado si dispone de un plan de datos limitados. Además, puede afectar negativamente a su velocidad de red. Considere todo esto si quiere habilitar %1$s. Eliminar ¿Eliminar «%1$s»? Se ha eliminado «%1$s» @@ -133,6 +136,7 @@ Editar método Editar nombre Habilitar + Habilitar de todos modos Usar servidor DNS personalizado Habilitar método Introducir MTU @@ -288,6 +292,7 @@ Restablecer todas las anulaciones No se puede configurar el servidor DNS del sistema. Envíe un informe de problemas. No se pueden aplicar las reglas del firewall. Intente solucionar el problema o envíe un informe de problemas. + Ajuste: %1$s Configuración Cuenta Acceso a API diff --git a/android/lib/resource/src/main/res/values-fi/strings.xml b/android/lib/resource/src/main/res/values-fi/strings.xml index f29073df1fe4..785dd2646ac9 100644 --- a/android/lib/resource/src/main/res/values-fi/strings.xml +++ b/android/lib/resource/src/main/res/values-fi/strings.xml @@ -74,6 +74,7 @@ Kyllä, kirjaa laite ulos Onnittelut! Suojaa yhteyteni + %1$s – %2$s käytössä Yhdistetään Yhdistetään Mullvad-järjestelmäpalveluun... Jatka kirjautumista @@ -100,6 +101,8 @@ Mukautettu WireGuard-portti Kelvolliset portit: %1$s Muokatun tunnelin isännän selvittäminen ei onnistu. Kokeile muuttaa asetuksiasi. + %1$s (%2$s) piilottaa salatussa VPN-liikenteessäsi toistuvat maneerit luomalla tarkoin räätälöityjä häiriöitä verkkoliikenteeseen ja tekemällä kaikista verkkopaketeista samankokoisia. Jos joku siis yrittää tarkkailla yhteyttäsi, hänen on huomattavasti vaikeampi tunnistaa, millä verkkosivustoilla oikein vierailet. + Huomioithan, että %1$s:n käyttöönotto kasvattaa verkkoliikennettäsi kokonaisuudessaan ja voi myös vaikuttaa negatiivisesti verkon nopeuteen. DAITA:n käyttämistä kannattaa harkita tarkkaan, mikäli datapakettisi tiedonsiirtomäärä on rajoitettu. Poista Poistetaanko \"%1$s\"? \"%1$s\" poistettiin @@ -133,6 +136,7 @@ Muokkaa menetelmää Muokkaa nimeä Ota käyttöön + Ota silti käyttöön Käytä mukautettua DNS-palvelinta Ota menetelmä käyttöön Syötä MTU @@ -288,6 +292,7 @@ Nollaa kaikki ohitukset Järjestelmän DNS-palvelimen asettaminen ei onnistu. Lähetä ongelmaraportti. Palomuurisääntöjä ei voida käyttää. Suorita vianetsintä tai lähetä ongelmaraportti. + Asetus: %1$s Asetukset Tili Ohjelmointirajapinnan käyttö diff --git a/android/lib/resource/src/main/res/values-fr/strings.xml b/android/lib/resource/src/main/res/values-fr/strings.xml index 6acc7c266088..9f214429f91f 100644 --- a/android/lib/resource/src/main/res/values-fr/strings.xml +++ b/android/lib/resource/src/main/res/values-fr/strings.xml @@ -74,6 +74,7 @@ Oui, déconnecter l\'appareil Félicitations ! Sécuriser ma connexion + %1$s utilisant %2$s Connexion Connexion au service système Mullvad... Continuer avec la connexion @@ -100,6 +101,8 @@ Port WireGuard personnalisé Plages valides : %1$s Échec de la résolution de l\'hôte du tunnel personnalisé. Essayez de modifier vos paramètres. + %1$s (%2$s) dissimule des motifs dans votre trafic VPN chiffré. Si quelqu\'un surveille votre connexion, il lui sera beaucoup plus difficile d\'identifier les sites Web que vous visitez. Pour ce faire, il ajoute soigneusement du bruit réseau et fait en sorte que tous les paquets de réseau aient la même taille. + Attention : étant donné que cette opération augmente le trafic total de votre réseau, faites attention si vous disposez d\'un forfait à données limitées. Cela peut également avoir un impact négatif sur la vitesse de votre réseau. Veuillez en tenir compte si vous souhaitez activer %1$s. Supprimer Supprimer « %1$s » ? « %1$s » a été supprimé @@ -133,6 +136,7 @@ Modifier le mode Modifier le nom Activer + Activer quand même Utiliser un serveur DNS personnalisé Activer la méthode Saisir le MTU @@ -288,6 +292,7 @@ Réinitialiser toutes les substitutions Impossible de définir le serveur DNS système. Veuillez envoyer un rapport de problème. Impossible d\'appliquer les règles du pare-feu. Merci de résoudre le problème ou d\'envoyer un rapport de problème. + Paramètre : %1$s Paramètres Compte Accès à l\'API diff --git a/android/lib/resource/src/main/res/values-it/strings.xml b/android/lib/resource/src/main/res/values-it/strings.xml index 1853a4e17108..20a8533616c2 100644 --- a/android/lib/resource/src/main/res/values-it/strings.xml +++ b/android/lib/resource/src/main/res/values-it/strings.xml @@ -74,6 +74,7 @@ Sì, disconnetti dal dispositivo Complimenti! Proteggi mia connessione + %1$s usa %2$s Connessione Connessione al servizio del sistema Mullvad... Continua con il login @@ -100,6 +101,8 @@ Porta personalizzata WireGuard Intervalli validi: %1$s Impossibile risolvere l\'host del tunnel personalizzato. Prova a modificare le impostazioni. + %1$s (%2$s) nasconde i percorsi in un traffico VPN crittografato. Se qualcuno sta monitorando la tua connessione, sarà molto più difficile per lui identificare quali siti web stai visitando. Ciò avviene aggiungendo con attenzione rumore di rete e rendendo tutti i pacchetti di rete della stessa dimensione. + Attenzione: poiché questa operazione aumenta il traffico di rete totale, usala con cautela se disponi di un piano dati limitato. Può anche avere un impatto negativo sulla velocità della rete. Tienilo presente se vuoi abilitare %1$s. Elimina Eliminare \"%1$s\"? \"%1$s\" eliminato @@ -133,6 +136,7 @@ Modifica metodo Modifica nome Abilita + Abilita comunque Usa un server DNS personalizzato Abilita metodo Inserisci MTU @@ -288,6 +292,7 @@ Reimposta tutte le sovrascritture Impossibile impostare il server DNS di sistema. Invia una segnalazione del problema. Impossibile applicare le regole del firewall. Consulta la risoluzione dei problemi o invia una segnalazione del problema. + Impostazione: %1$s Impostazioni Account Accesso API diff --git a/android/lib/resource/src/main/res/values-ja/strings.xml b/android/lib/resource/src/main/res/values-ja/strings.xml index 794ad8cc2861..6916c8de009e 100644 --- a/android/lib/resource/src/main/res/values-ja/strings.xml +++ b/android/lib/resource/src/main/res/values-ja/strings.xml @@ -74,6 +74,7 @@ はい。デバイスをログアウトさせます おめでとうございます! 接続を保護する + %1$s を使用した %2$s 接続中 Mullvad システムサービスに接続中... ログインを続ける @@ -100,6 +101,8 @@ WireGuardカスタムポート 有効な範囲: %1$s カスタムトンネルのホストを解決できません。設定を変更してみてください。 + %1$s (%2$s) を使用すると、暗号化された VPN トラフィックのパターンを隠すことができるようになります。何者かがあなたの接続を監視している場合に、アクセスしているウェブサイトの特定が大幅に難しくなります。ネットワークノイズを慎重に追加し、ネットワークパケットのサイズをすべて同一に揃えることによって実現しています。 + 注意: これにより、ネットワークトラフィックの総数が増加します。上限のあるデータプランをご利用の場合にはご注意ください。また、ネットワーク の速度にも悪影響を及ぼす可能性があります。%1$s を有効にする場合は、この点を考慮に入れてください。 削除 \"%1$s\" を削除しますか? \"%1$s\" は削除されました @@ -133,6 +136,7 @@ 方法の編集 名前を編集 有効にする + それでも有効化する カスタムDNSサーバーを使う 方法を有効化する MTU を入力 @@ -288,6 +292,7 @@ すべてのオーバーライドをリセット システムのDNSサーバーを設定できません。問題の報告を送信してください。 ファイアウォールのルールを適用できません。問題に対処するか、問題の報告を送信してください。 + 設定: %1$s 設定 アカウント APIアクセス diff --git a/android/lib/resource/src/main/res/values-ko/strings.xml b/android/lib/resource/src/main/res/values-ko/strings.xml index f80d45894361..e74820e81b08 100644 --- a/android/lib/resource/src/main/res/values-ko/strings.xml +++ b/android/lib/resource/src/main/res/values-ko/strings.xml @@ -74,6 +74,7 @@ 예, 장치에서 로그아웃 축하합니다! 내 연결 보안 유지 + %1$s 사용 %2$s 연결 중 Mullvad 시스템 서비스에 연결하는 중... 로그인 계속하기 @@ -100,6 +101,8 @@ WireGuard 사용자 지정 포트 유효한 범위: %1$s 사용자 지정 터널의 호스트를 확인할 수 없습니다. 설정을 변경해 보세요. + %1$s (%2$s)은(는) 암호화 VPN 트래픽에서 패턴을 숨깁니다. 누군가가 사용자의 연결을 모니터링하고 있다면, 사용자가 방문하고 있는 웹사이트 식별을 훨씬 더 어렵게 만듭니다. 네트워크 노이즈를 세심하게 추가하고, 모든 네트워크 패킷을 같은 크기로 만듭니다. + 주의: 이러한 방식은 총 네트워크 트래픽을 증가시키므로, 제한된 데이터 플랜 사용 시에는 유의하시기 바랍니다. 네트워크 속도에도 부정적 영향을 미칠 수 있습니다. %1$s 활성화 시에는 이러한 부분을 고려해 주시기 바랍니다. 삭제 \"%1$s\"을(를) 삭제하시겠습니까? \"%1$s\"이(가) 삭제되었습니다 @@ -133,6 +136,7 @@ 방법 편집 이름 편집 사용 + 그래도 사용 사용자 지정 DNS 서버 사용 방법 활성화 MTU 입력 @@ -288,6 +292,7 @@ 모든 재정의 초기화 시스템 DNS 서버를 설정할 수 없습니다. 문제 보고서를 보내주세요. 방화벽 규칙을 적용할 수 없습니다. 문제를 해결하거나 문제 보고서를 보내주세요. + 설정: %1$s 설정 계정 API 액세스 diff --git a/android/lib/resource/src/main/res/values-my/strings.xml b/android/lib/resource/src/main/res/values-my/strings.xml index d7a185edf43b..1b7561310ca6 100644 --- a/android/lib/resource/src/main/res/values-my/strings.xml +++ b/android/lib/resource/src/main/res/values-my/strings.xml @@ -74,6 +74,7 @@ စက်မှ ထွက်မည် ဝမ်းသာပါတယ်။ ကျွန်ုပ်၏ ချိတ်ဆက်မှုကို ကာကွယ်ရန် + %1$s သည် %2$s ကို သုံးနေပါသည် ချိတ်ဆက်နေဆဲ Mullvad စနစ် ဝန်ဆောင်မှုနှင့် ချိတ်ဆက်နေဆဲ... ဆက်လက် ဝင်ရောက်ရန် @@ -100,6 +101,8 @@ စိတ်ကြိုက် WireGuard ပေါ့တ် အကျုံးဝင်သည့် အပိုင်းအခြား- %1$s စိတ်ကြိုက်ပြုလုပ်ထားသည့် Tunnel ၏ Host ကို ဖြေရှင်း၍ မရနိုင်ပါ။ သင့်ဆက်တင်ကို ပြောင်းကြည့်ပါ။ + %1$s (%2$s) သည် သင်၏ ကုဒ်ပြောင်းဝှက်ထားသော VPN ကူးလူးမှုတွင် ပက်တန်များကို ဝှက်ထားပါသည်။ သင့်ချိတ်ဆက်မှုကို တစ်စုံတစ်ယောက်က စောင့်ကြည့်နေပါက မည်သည့်ဝက်ဘ်ဆိုက်များ သင်ဝင်ရောက်နေသည်ကို ၎င်းတို့က ခွဲခြားဖော်ထုတ်ဖို့ရာ ပို၍ သိသိသာသာ ခက်ခဲသွားအောင် ၎င်းက ပြုလုပ်ပေးပါသည်။ ကွန်ရက် အနှောင့်အယှက်လျှပ်လိုင်းကို ဂရုတစိုက် ထည့်ပြီး ကွန်ရက် ပက်ကက်အားလုံးကို အရွယ်အစားတူညီအောင် ပြုလုပ်ခြင်းဖြင့် ပို၍ခက်ခဲသွားအောင် ဆောင်ရွက်ပါသည်။ + သတိပြုရန်- ဤသည်က သင့်ကွန်ရန်ကူးလူးမှု စုစုပေါင်းပမာဏကို တိုးစေသောကြောင့် ဒေတာပလန်သည် အကန့်အသတ်ရှိပါက သတိထားဆောင်ရွက်ပါ။ သင့်ကွန်ရက်အမြန်နှုန်းကိုလည်း အပျက်သဘောထိခိုက်စေနိုင်ပါသည်။ %1$s ကို ဖွင့်လိုပါက ဤသည်ကို ထည့်သွင်းစဉ်းစားပေးပါ။ ဖျက်ရန် \"%1$s\" ကို ဖျက်မည်လား။ \"%1$s\" ကို ဖျက်ပြီးပါပြီ @@ -133,6 +136,7 @@ နည်းလမ်းကို တည်းဖြတ်ပါ အမည် တည်းဖြတ်ရန် ဖွင့်ရန် + မည်သို့ပင်ဖြစ်စေ ဖွင့်ရန် စိတ်ကြိုက် DNS ဆာဗာကို သုံးရန် နည်းလမ်းကို ဖွင့်ရန် MTU ကို ရိုက်ထည့်ရန် @@ -288,6 +292,7 @@ ကျော်လွန် ပယ်ဖျက်မှု အားလုံးကို ပြန်လည်သတ်မှတ်ရန် စနစ် DNS ဆာဗာကို သတ်မှတ်၍ မရနိုင်ပါ။ ပြဿနာ ရီပို့တ်တစ်ခု ပေးပို့ပေးပါ။ Firewall စည်းမျဉ်းများကို အသုံးချ၍ မရနိုင်ပါ။ ပြစ်ချက် ရှာဖွေဖယ်ရှာပေးပါ သို့မဟုတ် ပြဿနာ ရီပို့တ် ပေးပို့ပေးပါ။ + ဆက်တင်- %1$s ဆက်တင် အကောင့် API ရယူသုံးစွဲခွင့် diff --git a/android/lib/resource/src/main/res/values-nb/strings.xml b/android/lib/resource/src/main/res/values-nb/strings.xml index 4be52ec77397..ac085f9269b4 100644 --- a/android/lib/resource/src/main/res/values-nb/strings.xml +++ b/android/lib/resource/src/main/res/values-nb/strings.xml @@ -74,6 +74,7 @@ Ja, logg av enhet Gratulerer! Gjør tilkoblingen sikker + %1$s bruker %2$s Kobler til Kobler til Mullvads systemtjeneste ... Fortsett med pålogging @@ -100,6 +101,8 @@ Tilpasset WireGuard-port Gyldige verdiområder: %1$s Kunne ikke løse vert for egendefinert tunnel. Forsøk å endre innstillingene dine. + %1$s (%2$s) skjuler mønstre i den krypterte VPN-trafikken. Hvis noen overvåker tilkoblingen din, gjør dette det betydelig vanskeligere for dem å identifisere hvilke nettsteder du besøker. Dette gjøres ved varsomt å legge til nettverksstøy og gjøre alle nettverkspakker like store. + Obs: Siden dette øker den totale nettverkstrafikken, bør du være forsiktig hvis du har et abonnement med begrenset data. Det kan også påvirke nettverkshastigheten i negativ retning. Vær oppmerksom på dette hvis du ønsker å aktivere %1$s. Slett Slette «%1$s»? «%1$s» ble slettet @@ -133,6 +136,7 @@ Endre metode Endre navn Aktiver + Aktiver uansett Bruk egendefinert DNS-server Aktiver metoden Angi MTU @@ -288,6 +292,7 @@ Tilbakestill alle overstyringer Kunne ikke angi DNS-server for systemet. Send inn en problemrapport. Kunne ikke bruke brannmur-regler. Feilsøk eller send inn en problemrapport. + Innstilling: %1$s Innstillinger Konto API-tilgang diff --git a/android/lib/resource/src/main/res/values-nl/strings.xml b/android/lib/resource/src/main/res/values-nl/strings.xml index df7414265b53..a5462c93b766 100644 --- a/android/lib/resource/src/main/res/values-nl/strings.xml +++ b/android/lib/resource/src/main/res/values-nl/strings.xml @@ -74,6 +74,7 @@ Ja, apparaat afmelden Gefeliciteerd! Mijn verbinding beveiligen + %1$s via %2$s Bezig met verbinden Verbinden met Mullvad-systeemdienst... Doorgaan met aanmelden @@ -100,6 +101,8 @@ Aangepaste WireGuard-poort Geldige bereiken: %1$s Kan host van aangepaste tunnel niet omzetten. Probeer uw instellingen te wijzigen. + %1$s (%2$s) verbergt patronen in het versleutelde VPN-verkeer. Als iemand de verbinding in de gaten houdt, maakt dit het aanzienlijk moeilijker voor diegene om te zien welke websites u bezoekt. Dit wordt gedaan door zorgvuldig netwerkruis toe te voegen en alle netwerkpakketten even groot te maken. + Let op: omdat dit het totale netwerkverkeer verhoogt, moet u voorzichtig zijn als u een beperkt data-abonnement hebt. Ook kan het uw netwerksnelheid negatief beïnvloeden. Houd hier rekening mee als u %1$s wilt inschakelen. Verwijderen \"%1$s\" verwijderen? \"%1$s\" is verwijderd @@ -133,6 +136,7 @@ Methode bewerken Naam bewerken Inschakelen + Toch inschakelen Aangepaste DNS-server gebruiken Methode inschakelen Voer MTU in @@ -288,6 +292,7 @@ Alle overschrijvingen resetten Kan DNS-server van systeem niet instellen. Stuur een probleemrapport. Kan firewallregels niet toepassen. Los problemen op of stuur een probleemmelding. + Instelling: %1$s Instellingen Account API-toegang diff --git a/android/lib/resource/src/main/res/values-pl/strings.xml b/android/lib/resource/src/main/res/values-pl/strings.xml index 9c3fb6b485db..b97c1e62fe7e 100644 --- a/android/lib/resource/src/main/res/values-pl/strings.xml +++ b/android/lib/resource/src/main/res/values-pl/strings.xml @@ -74,6 +74,7 @@ Tak, wyloguj urządzenie Gratulacje! Zabezpiecz moje połączenie + %1$s przy użyciu %2$s Łączenie Łączenie z usługą systemową Mullvad... Kontynuuj logowanie @@ -100,6 +101,8 @@ Niestandardowy port WireGuard Prawidłowe zakresy: %1$s Nie można rozpoznać hosta tunelu niestandardowego. Spróbuj zmienić ustawienia. + %1$s (%2$s) ukrywa wzorce w zaszyfrowanym ruchu VPN. Jeśli ktoś monitoruje Twoje połączenie, znacznie utrudni to identyfikację odwiedzanych witryn. Odbywa się to poprzez ostrożne dodawanie szumów sieciowych i ustawianie tego samego rozmiaru wszystkich pakietów sieciowych. + Uwaga: zwiększa to łączny ruch sieciowy, zachowaj więc ostrożność, jeśli masz ograniczony pakiet danych. Może to również negatywnie wpłynąć na szybkość sieci. Weź to pod uwagę, jeśli chcesz włączyć %1$s. Usuń Usunąć „%1$s”? Usunięto „%1$s” @@ -133,6 +136,7 @@ Edytuj metodę Edytuj nazwę Włącz + Mimo to włącz Użyj niestandardowego serwera DNS Włącz metodę Wprowadź MTU @@ -288,6 +292,7 @@ Resetuj wszystkie zastąpienia Nie można ustawić systemowego serwera DNS. Wyślij zgłoszenie problemu. Nie można zastosować reguł zapory. Rozwiąż problem lub wyślij zgłoszenie problemu. + Ustawienie: %1$s Ustawienia Konto Dostęp do API diff --git a/android/lib/resource/src/main/res/values-pt/strings.xml b/android/lib/resource/src/main/res/values-pt/strings.xml index 5482bd0cac38..559e92a6b510 100644 --- a/android/lib/resource/src/main/res/values-pt/strings.xml +++ b/android/lib/resource/src/main/res/values-pt/strings.xml @@ -74,6 +74,7 @@ Sim, desligar o dispositivo Parabéns! Tornar a minha ligação segura + %1$s a usar %2$s A ligar A ligar-se ao serviço de sistema Mulvad... Continuar com a ligação @@ -100,6 +101,8 @@ Porta personalizada WireGuard Intervalos válidos: %1$s Não foi possível resolver o anfitrião do túnel personalizado. Experimente alterar as suas definições. + %1$s (%2$s) oculta padrões no seu tráfego VPN encriptado. Se alguém estiver a monitorizar a sua ligação, isto dificulta significativamente a identificação dos sites que visita. Para tal, adiciona cuidadosamente ruído de rede e torna todos os pacotes de rede do mesmo tamanho. + Atenção: visto que isto aumenta o tráfego total da sua rede, tenha cuidado se tiver um plano de dados limitado. Também pode afetar negativamente a velocidade da sua rede. Tenha isto em consideração se pretender ativar %1$s. Eliminar Eliminar \"%1$s\"? \"%1$s\" foi eliminada @@ -133,6 +136,7 @@ Editar método Editar nome Ativar + Ativar mesmo assim Usar servidor DNS personalizado Ativar método Introduzir MTU @@ -288,6 +292,7 @@ Repor todas as substituições Não foi possível definir o servidor DNS do sistema. Envie um relatório do problema. Não foi possível aplicar as regras de firewall. Experimente a resolução de problemas ou envie um relatório do problema. + Definição: %1$s Definições Conta Acesso à API diff --git a/android/lib/resource/src/main/res/values-ru/strings.xml b/android/lib/resource/src/main/res/values-ru/strings.xml index 97269c75ed44..b930f03c14f9 100644 --- a/android/lib/resource/src/main/res/values-ru/strings.xml +++ b/android/lib/resource/src/main/res/values-ru/strings.xml @@ -74,6 +74,7 @@ Выйти из профиля на устройстве Поздравляем! Защитить мое подключение + %1$s через %2$s Идет подключение Подключение к системному сервису Mullvad... Войти @@ -100,6 +101,8 @@ Пользовательский порт WireGuard Допустимые диапазоны: %1$s Не удалось преобразовать имя узла пользовательского туннеля. Попробуйте изменить настройки. + %1$s (%2$s) маскирует особенности зашифрованного VPN-трафика. Если кто-то следит за вашим подключением, ему будет значительно сложнее определить, какие сайты вы посещаете. Для этого добавляется сетевой шум, а все сетевые пакеты делаются одинаковыми по размеру. + Внимание: использование этой функции увеличит общий сетевой трафик — учтите это при ограниченном тарифном плане. Это также может негативно повлиять на скорость работы сети. Помните об этих факторах при включении %1$s. Удалить Удалить список «%1$s»? Список «%1$s» удален @@ -133,6 +136,7 @@ Изменение метода Изменить имя Включить + Всё равно включить Пользовательский DNS-сервер Включить метод Введите MTU @@ -288,6 +292,7 @@ Сброс всех переопределений Не удалось установить системный DNS-сервер. Отправьте сообщение о проблеме. Невозможно применить правила брандмауэра. Устраните неполадки или отправьте сообщение о проблеме. + Параметр: %1$s Настройки Учетная запись Доступ к API diff --git a/android/lib/resource/src/main/res/values-sv/strings.xml b/android/lib/resource/src/main/res/values-sv/strings.xml index 3395eeb6e601..6c34428c07a1 100644 --- a/android/lib/resource/src/main/res/values-sv/strings.xml +++ b/android/lib/resource/src/main/res/values-sv/strings.xml @@ -74,6 +74,7 @@ Ja, logga ut enheten Grattis! Skydda min anslutning + %1$s använder %2$s Ansluter Ansluter till Mullvads systemtjänst... Fortsätt med inloggning @@ -100,6 +101,8 @@ Anpassad WireGuard-port Giltiga intervall: %1$s Det går inte att lösa värd för anpassad tunnel. Försök att ändra inställningarna. + %1$s (%2$s) döljer mönster i din krypterade VPN-trafik. Om någon övervakar din anslutning blir det mycket svårare för hen att identifiera vilka webbplatser du besöker. Den gör det genom att noggrant lägga till nätverksbrus och se till så att alla nätverkspaket har samma storlek. + Obs! Detta ger dig högre total nätverkstrafik så var försiktig på om du har ett abonnemang med begränsad mängd data. Det kan även ha en negativ påverkan på din nätverkshastighet. Tänk på det när du aktiverar %1$s. Ta bort Ta bort \"%1$s\"? \"%1$s\" har tagits bort @@ -133,6 +136,7 @@ Redigera metod Redigera namn Aktivera + Aktivera ändå Använd anpassad DNS-server Aktivera metod Ange MTU @@ -288,6 +292,7 @@ Återställ alla åsidosättningar Det går inte att konfigurera DNS-server. Skicka en problemrapport. Det går inte att tillämpa brandväggsregler. Felsök eller skicka en problemrapport. + Inställning: %1$s Inställningar Konto API-åtkomst diff --git a/android/lib/resource/src/main/res/values-th/strings.xml b/android/lib/resource/src/main/res/values-th/strings.xml index 6873f09ab009..cd9053dc002d 100644 --- a/android/lib/resource/src/main/res/values-th/strings.xml +++ b/android/lib/resource/src/main/res/values-th/strings.xml @@ -74,6 +74,7 @@ ใช่ นำอุปกรณ์ออกจากระบบ ยินดีด้วย! รักษาความปลอดภัยในการเชื่อมต่อ + %1$s กำลังใช้ %2$s กำลังเชื่อมต่อ กำลังเชื่อมต่อบริการของระบบ Mullvad... เข้าสู่ระบบต่อ @@ -100,6 +101,8 @@ พอร์ต WireGuard แบบกำหนดเอง ช่วงที่ใช้ได้: %1$s ไม่พบโฮสต์ของช่องทางแบบกำหนดเอง กรุณาลองเปลี่ยนการตั้งค่าของคุณ + %1$s (%2$s) ซ่อนรูปแบบในการรับส่งข้อมูล VPN ที่เข้ารหัสของคุณ หากมีใครกำลังเฝ้าดูการเชื่อมต่อของคุณอยู่ สิ่งนี้จะทำให้การระบุเว็บไซต์ที่คุณกำลังเยี่ยมชมยากขึ้นอย่างมาก ซึ่งทำได้โดยการเพิ่มสัญญาณรบกวนเครือข่ายอย่างระมัดระวัง และทำให้แพ็กเก็ตเครือข่ายทั้งหมดมีขนาดเท่ากันหมด + โปรดทราบ: เนื่องจากการดำเนินการนี้จะเพิ่มการรับส่งข้อมูลโดยรวมของคุณขึ้น โปรดใช้ความระมัดระวัง หากคุณมีแผนข้อมูลที่มีปริมาณจำกัด การดำเนินการนี้อาจส่งผลเชิงลบต่อความเร็วเครือข่ายของคุณได้ โปรดพิจารณาข้อมูลเหล่านี้ หากคุณต้องการเปิดใช้งาน %1$s ลบ ลบ \"%1$s\" หรือไม่ \"%1$s\" ถูกลบแล้ว @@ -133,6 +136,7 @@ แก้ไขวิธี แก้ไขชื่อ เปิดใช้งาน + เปิดใช้งานต่อไป ใช้เซิร์ฟเวอร์ DNS แบบกำหนดเอง เปิดใช้งานวิธีการ ป้อน MTU @@ -288,6 +292,7 @@ รีเซ็ตโอเวอร์ไรด์ทั้งหมด ไม่สามารถตั้งค่าเซิร์ฟเวอร์ DNS ของระบบได้ โปรดส่งรายงานปัญหา ไม่สามารถใช้กฎไฟร์วอลล์ได้ โปรดแก้ไขปัญหา หรือส่งรายงานปัญหา + การตั้งค่า: %1$s การตั้งค่า บัญชี การเข้าถึง API diff --git a/android/lib/resource/src/main/res/values-tr/strings.xml b/android/lib/resource/src/main/res/values-tr/strings.xml index b0d7207d21f3..d1f31fc53a65 100644 --- a/android/lib/resource/src/main/res/values-tr/strings.xml +++ b/android/lib/resource/src/main/res/values-tr/strings.xml @@ -74,6 +74,7 @@ Evet, cihazdan çıkış yap Tebrikler! Bağlantımı güvenceye al + %1$s, %2$s kullanıyor Bağlanılıyor Mullvad sistem hizmetlerine bağlanılıyor... Giriş yapmak için devam et @@ -100,6 +101,8 @@ WireGuard özel portu Geçerli aralıklar: %1$s Özel tünel ana bilgisayarı çözülemedi. Ayarlarınızı değiştirmeyi deneyin. + %1$s (%2$s), şifrelenmiş VPN trafiğinizdeki kalıpları gizler. Bu sayede, başka biri bağlantınızı izliyorsa ziyaret ettiğiniz web sitelerini tespit etmesi çok daha zor olacaktır. Özellik, dikkatli bir şekilde ağ paraziti ekleyerek ve tüm ağ paketlerini aynı boyuta getirerek sizi izlenmekten korur. + Dikkat: Bu özellik, toplam ağ trafiğinizi artıracağından dolayı sınırlı bir veri planınız varsa dikkatli olun. Bu, ağ hızınızı da olumsuz etkileyebilir. %1$s özelliğini etkinleştirmek istiyorsanız lütfen bu durumu göz önünde bulundurun. Sil \"%1$s\" silinsin mi\"? \"%1$s\" silindi @@ -133,6 +136,7 @@ Yöntemi düzenle Adı düzenle Etkinleştir + Yine de etkinleştir Özel DNS sunucusu kullanın Yöntemi etkinleştir MTU\'yu girin @@ -288,6 +292,7 @@ Tüm geçersiz kılmaları sıfırla Sistem DNS sunucusu ayarlanamıyor. Lütfen bir hata raporu gönderin. Güvenlik duvarı kuralları uygulanamıyor. Lütfen sorunu çözmeye çalışın veya bir hata raporu gönderin. + Ayar: %1$s Ayarlar Hesap API erişimi diff --git a/android/lib/resource/src/main/res/values-zh-rCN/strings.xml b/android/lib/resource/src/main/res/values-zh-rCN/strings.xml index ff6879f9b8d0..222b9c007dab 100644 --- a/android/lib/resource/src/main/res/values-zh-rCN/strings.xml +++ b/android/lib/resource/src/main/res/values-zh-rCN/strings.xml @@ -74,6 +74,7 @@ 是,退出设备 恭喜! 保护我的连接 + %1$s,使用 %2$s 正在连接 正在连接到 Mullvad 系统服务… 继续登录 @@ -100,6 +101,8 @@ WireGuard 自定义端口 有效范围:%1$s 无法解析自定义隧道的主机。请尝试更改您的设置。 + %1$s(%2$s)会在您的加密 VPN 流量中隐藏模式。如果有人在监视您的连接,这可以让他们很难识别您正在访问的网站。它的实现方法是小心地添加网络噪声并使所有网络数据包的大小都相同。 + 注意:这会增加您的总网络流量,因此如果您的流量套餐有限,请谨慎使用。它还会对您的网速产生负面影响。如果要启用 %1$s,请考虑到这一点。 删除 删除“%1$s”? “%1$s”已被删除 @@ -133,6 +136,7 @@ 编辑方法 编辑名称 启用 + 仍然启用 使用自定义 DNS 服务器 启用方法 输入 MTU @@ -288,6 +292,7 @@ 重置所有覆盖设置 无法设置系统 DNS 服务器。请发送问题报告。 无法应用防火墙规则。请排查问题或发送问题报告。 + 设置:%1$s 设置 帐户 API 访问 diff --git a/android/lib/resource/src/main/res/values-zh-rTW/strings.xml b/android/lib/resource/src/main/res/values-zh-rTW/strings.xml index fe531572b895..ed70785bb14f 100644 --- a/android/lib/resource/src/main/res/values-zh-rTW/strings.xml +++ b/android/lib/resource/src/main/res/values-zh-rTW/strings.xml @@ -74,6 +74,7 @@ 是,將裝置登出 恭喜! 保護我的連線 + %1$s,使用 %2$s 正在連線 連線 Mullvad 系統服務中... 繼續登入 @@ -100,6 +101,8 @@ WireGuard 自訂連接埠 有效範圍:%1$s 無法解析自訂通道的主機。請嘗試變更您的設定。 + %1$s (%2$s) 會在您的加密 VPN 流量中隱藏模式。如果有人正在監視您的連線,這能讓他們難以識別出您正在存取的網站。此實現方式係謹慎加入網路噪音,並使所有網路資料包大小皆相同。 + 注意:這會增加您的網路總流量。如果您方案中的流量有限,請謹慎使用。這還會對您的網路速度產生負面影響。如果要啟用 %1$s,請考慮到這一點。 刪除 要刪除「%1$s」嗎? 「%1$s」已刪除 @@ -133,6 +136,7 @@ 編輯方式 編輯名稱 啟用 + 仍然啟用 使用自訂 DNS 伺服器 啟用方式 輸入 MTU @@ -288,6 +292,7 @@ 重設所有覆寫設定 無法設定系統 DNS 伺服器。請傳送問題回報。 無法套用防火牆規則。請排除故障或傳送問題回報。 + 設定:%1$s 設定 帳戶 API 存取 diff --git a/gui/locales/messages.pot b/gui/locales/messages.pot index d35bfa0453e9..1d70b92ac5e2 100644 --- a/gui/locales/messages.pot +++ b/gui/locales/messages.pot @@ -2163,6 +2163,9 @@ msgstr "" msgid "%s (added)" msgstr "" +msgid "%s using %s" +msgstr "" + msgid "%s was added to \"%s\"" msgstr "" From fca422d5f58e66ad8fc364b2ae3d393d3c9ec5e3 Mon Sep 17 00:00:00 2001 From: Albin Date: Thu, 5 Sep 2024 23:58:31 +0200 Subject: [PATCH 6/6] Install cbindgen in build script and workflow Temporary fix to address maybenot.h (checked in) sometimes needing to be re-generated due to how `make` looks at last-modifications while git neither stores nor consistently sets modification metadata on file checkout. NOTE: The version should match the one used in the checked in maybenot.h file. --- .github/workflows/android-app.yml | 2 ++ build-apk.sh | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/.github/workflows/android-app.yml b/.github/workflows/android-app.yml index 204b84ed5a40..b098d346f79c 100644 --- a/.github/workflows/android-app.yml +++ b/.github/workflows/android-app.yml @@ -206,6 +206,8 @@ jobs: RUSTFLAGS: --deny warnings BUILD_TYPE: debug run: | + # Temporary fix to address maybenot.h build issues. + cargo install --force cbindgen --version "0.26.0" ARCHITECTURES="${{ matrix.abi }}" UNSTRIPPED_LIB_PATH="$CARGO_TARGET_DIR/${{ matrix.target }}/$BUILD_TYPE/libmullvad_jni.so" STRIPPED_LIB_PATH="./android/app/build/extraJni/${{ matrix.abi }}/libmullvad_jni.so" diff --git a/build-apk.sh b/build-apk.sh index e45d4014c9d4..748baa0946b8 100755 --- a/build-apk.sh +++ b/build-apk.sh @@ -77,6 +77,14 @@ mkdir -p "app/build/extraAssets" mkdir -p "app/build/extraJni" popd +# Temporary fix to address maybenot.h (checked in) sometimes needing to be +# re-generated due to how `make` looks at last-modifications while git neither +# stores nor consistently sets modification metadata on file checkout. +# +# NOTE: The version should match the one used in the checked in +# maybenot.h file. +cargo install --force cbindgen --version "0.26.0" + for ARCHITECTURE in ${ARCHITECTURES:-aarch64 armv7 x86_64 i686}; do case "$ARCHITECTURE" in "x86_64")