summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorKalle Lindström <karl.lindstrom@mullvad.net>2026-04-22 09:02:59 +0200
committerKalle Lindström <karl.lindstrom@mullvad.net>2026-04-22 13:57:44 +0200
commitf5d2975f1e62c19a772ae29b89edf2c6b79c4141 (patch)
treef83f439e3abf4594ff988073debfc40d78247ce1
parent9fb40393097ba2c2619f6873cedcc479237b4d86 (diff)
downloadmullvadvpn-f5d2975f1e62c19a772ae29b89edf2c6b79c4141.tar.xz
mullvadvpn-f5d2975f1e62c19a772ae29b89edf2c6b79c4141.zip
Show number of excluded split tunneling apps
-rw-r--r--android/lib/feature/splittunneling/impl/build.gradle.kts1
-rw-r--r--android/lib/feature/splittunneling/impl/src/main/java/net/mullvad/mullvadvpn/feature/splittunneling/impl/SplitTunnelingScreen.kt36
-rw-r--r--android/lib/feature/splittunneling/impl/src/main/java/net/mullvad/mullvadvpn/feature/splittunneling/impl/search/SearchSplitTunnelingScreen.kt5
-rw-r--r--android/lib/ui/designsystem/src/main/kotlin/net/mullvad/mullvadvpn/lib/ui/designsystem/ListHeader.kt19
-rw-r--r--android/lib/ui/resource/src/main/res/values/strings.xml1
-rw-r--r--android/lib/ui/util/build.gradle.kts2
-rw-r--r--android/lib/ui/util/src/main/kotlin/net/mullvad/mullvadvpn/lib/ui/util/Modifier.kt11
7 files changed, 64 insertions, 11 deletions
diff --git a/android/lib/feature/splittunneling/impl/build.gradle.kts b/android/lib/feature/splittunneling/impl/build.gradle.kts
index 66f2072c5f..2fd10ce3f9 100644
--- a/android/lib/feature/splittunneling/impl/build.gradle.kts
+++ b/android/lib/feature/splittunneling/impl/build.gradle.kts
@@ -14,4 +14,5 @@ dependencies {
implementation(libs.koin.compose)
implementation(libs.arrow)
implementation(projects.lib.feature.splittunneling.api)
+ implementation(projects.lib.ui.util)
}
diff --git a/android/lib/feature/splittunneling/impl/src/main/java/net/mullvad/mullvadvpn/feature/splittunneling/impl/SplitTunnelingScreen.kt b/android/lib/feature/splittunneling/impl/src/main/java/net/mullvad/mullvadvpn/feature/splittunneling/impl/SplitTunnelingScreen.kt
index 1f992f6069..e9b81aa249 100644
--- a/android/lib/feature/splittunneling/impl/src/main/java/net/mullvad/mullvadvpn/feature/splittunneling/impl/SplitTunnelingScreen.kt
+++ b/android/lib/feature/splittunneling/impl/src/main/java/net/mullvad/mullvadvpn/feature/splittunneling/impl/SplitTunnelingScreen.kt
@@ -64,6 +64,7 @@ import net.mullvad.mullvadvpn.lib.ui.theme.Dimens
import net.mullvad.mullvadvpn.lib.ui.theme.color.AlphaDisabled
import net.mullvad.mullvadvpn.lib.ui.theme.color.AlphaScrollbar
import net.mullvad.mullvadvpn.lib.ui.theme.color.AlphaVisible
+import net.mullvad.mullvadvpn.lib.ui.util.visible
import org.koin.androidx.compose.koinViewModel
import org.koin.core.parameter.parametersOf
@@ -225,10 +226,12 @@ private fun LazyListScope.appList(
onResolveIcon: (PackageName) -> Drawable?,
) {
if (state.excludedApps.isNotEmpty()) {
- headerItem(
+ excludedAppsHeaderItem(
key = SplitTunnelingContentKey.EXCLUDED_APPLICATIONS,
textId = R.string.exclude_applications,
enabled = state.enabled,
+ exludedAppsCount = state.excludedApps.size,
+ includedAppsCount = state.includedApps.size,
)
appItems(
apps = state.excludedApps,
@@ -320,16 +323,29 @@ internal fun LazyListScope.appItems(
internal fun LazyListScope.headerItem(key: String, textId: Int, enabled: Boolean) {
itemWithDivider(key = key, contentType = ContentType.HEADER) {
ListHeader(
- modifier =
- Modifier.animateItem()
- .alpha(
- if (enabled) {
- AlphaVisible
- } else {
- AlphaDisabled
- }
- ),
+ modifier = Modifier.animateItem().visible(enabled),
+ text = stringResource(id = textId),
+ )
+ }
+}
+
+internal fun LazyListScope.excludedAppsHeaderItem(
+ key: String,
+ textId: Int,
+ enabled: Boolean,
+ exludedAppsCount: Int,
+ includedAppsCount: Int,
+) {
+ itemWithDivider(key = key, contentType = ContentType.HEADER) {
+ ListHeader(
+ modifier = Modifier.animateItem().visible(enabled),
text = stringResource(id = textId),
+ trailingText =
+ stringResource(
+ R.string.x_out_of_y,
+ exludedAppsCount,
+ exludedAppsCount + includedAppsCount,
+ ),
)
}
}
diff --git a/android/lib/feature/splittunneling/impl/src/main/java/net/mullvad/mullvadvpn/feature/splittunneling/impl/search/SearchSplitTunnelingScreen.kt b/android/lib/feature/splittunneling/impl/src/main/java/net/mullvad/mullvadvpn/feature/splittunneling/impl/search/SearchSplitTunnelingScreen.kt
index 494757f35f..4ef0597de4 100644
--- a/android/lib/feature/splittunneling/impl/src/main/java/net/mullvad/mullvadvpn/feature/splittunneling/impl/search/SearchSplitTunnelingScreen.kt
+++ b/android/lib/feature/splittunneling/impl/src/main/java/net/mullvad/mullvadvpn/feature/splittunneling/impl/search/SearchSplitTunnelingScreen.kt
@@ -40,6 +40,7 @@ import net.mullvad.mullvadvpn.feature.splittunneling.impl.CommonContentKey
import net.mullvad.mullvadvpn.feature.splittunneling.impl.ContentType
import net.mullvad.mullvadvpn.feature.splittunneling.impl.SplitTunnelingContentKey
import net.mullvad.mullvadvpn.feature.splittunneling.impl.appItems
+import net.mullvad.mullvadvpn.feature.splittunneling.impl.excludedAppsHeaderItem
import net.mullvad.mullvadvpn.feature.splittunneling.impl.getApplicationIconOrNull
import net.mullvad.mullvadvpn.feature.splittunneling.impl.headerItem
import net.mullvad.mullvadvpn.lib.common.Lc
@@ -158,10 +159,12 @@ private fun LazyListScope.appList(
item { NoAppsMatchingSearch(state.searchTerm) }
}
if (state.excludedApps.isNotEmpty()) {
- headerItem(
+ excludedAppsHeaderItem(
key = SplitTunnelingContentKey.EXCLUDED_APPLICATIONS,
textId = R.string.exclude_applications,
enabled = true,
+ exludedAppsCount = state.excludedApps.size,
+ includedAppsCount = state.includedApps.size,
)
appItems(
apps = state.excludedApps,
diff --git a/android/lib/ui/designsystem/src/main/kotlin/net/mullvad/mullvadvpn/lib/ui/designsystem/ListHeader.kt b/android/lib/ui/designsystem/src/main/kotlin/net/mullvad/mullvadvpn/lib/ui/designsystem/ListHeader.kt
index 9f25242965..c36ff60227 100644
--- a/android/lib/ui/designsystem/src/main/kotlin/net/mullvad/mullvadvpn/lib/ui/designsystem/ListHeader.kt
+++ b/android/lib/ui/designsystem/src/main/kotlin/net/mullvad/mullvadvpn/lib/ui/designsystem/ListHeader.kt
@@ -3,9 +3,11 @@ package net.mullvad.mullvadvpn.lib.ui.designsystem
import androidx.compose.foundation.layout.IntrinsicSize
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope
+import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.defaultMinSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.width
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.Edit
import androidx.compose.material3.HorizontalDivider
@@ -29,6 +31,22 @@ fun ListHeader(text: String, modifier: Modifier = Modifier) {
}
@Composable
+fun ListHeader(modifier: Modifier = Modifier, text: String, trailingText: String) {
+ ListHeader(
+ modifier = modifier,
+ content = { Text(text = text) },
+ actions = {
+ Spacer(Modifier.width(Dimens.smallSpacer))
+ Text(
+ text = trailingText,
+ style = MaterialTheme.typography.bodyMedium,
+ color = MaterialTheme.colorScheme.onSurfaceVariant,
+ )
+ },
+ )
+}
+
+@Composable
fun ListHeader(
content: @Composable () -> Unit,
modifier: Modifier = Modifier,
@@ -75,4 +93,5 @@ fun PreviewListHeader() =
}
},
)
+ ListHeader(text = "Header", trailingText = "3 out of 200")
}
diff --git a/android/lib/ui/resource/src/main/res/values/strings.xml b/android/lib/ui/resource/src/main/res/values/strings.xml
index 4d92f6c27b..96f0b13de3 100644
--- a/android/lib/ui/resource/src/main/res/values/strings.xml
+++ b/android/lib/ui/resource/src/main/res/values/strings.xml
@@ -132,6 +132,7 @@
<string name="custom_dns_footer">Enable to add at least one DNS server.</string>
<string name="confirm_local_dns">The local DNS server will not work unless you enable \"Local Network Sharing\" under VPN settings.</string>
<string name="exclude_applications">Excluded applications</string>
+ <string name="x_out_of_y">%1$d out of %2$d</string>
<string name="all_applications">All applications</string>
<string name="show_system_apps">Show system apps</string>
<string name="toggle_vpn">Toggle VPN</string>
diff --git a/android/lib/ui/util/build.gradle.kts b/android/lib/ui/util/build.gradle.kts
index ede0822b03..48fa3b2ac9 100644
--- a/android/lib/ui/util/build.gradle.kts
+++ b/android/lib/ui/util/build.gradle.kts
@@ -14,4 +14,6 @@ dependencies {
implementation(libs.compose.ui.tooling.preview)
implementation(libs.compose.material3)
implementation(libs.compose.icons.extended)
+
+ implementation(projects.lib.ui.theme)
}
diff --git a/android/lib/ui/util/src/main/kotlin/net/mullvad/mullvadvpn/lib/ui/util/Modifier.kt b/android/lib/ui/util/src/main/kotlin/net/mullvad/mullvadvpn/lib/ui/util/Modifier.kt
index e659e68731..2cff4ff545 100644
--- a/android/lib/ui/util/src/main/kotlin/net/mullvad/mullvadvpn/lib/ui/util/Modifier.kt
+++ b/android/lib/ui/util/src/main/kotlin/net/mullvad/mullvadvpn/lib/ui/util/Modifier.kt
@@ -2,6 +2,9 @@ package net.mullvad.mullvadvpn.lib.ui.util
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.alpha
+import net.mullvad.mullvadvpn.lib.ui.theme.color.AlphaInvisible
+import net.mullvad.mullvadvpn.lib.ui.theme.color.AlphaVisible
@Composable
fun <T> Modifier.applyIfNotNull(
@@ -22,3 +25,11 @@ fun Modifier.applyIf(condition: Boolean, block: @Composable Modifier.() -> Modif
} else {
this
}
+
+@Composable
+fun Modifier.visible(visible: Boolean): Modifier =
+ if (visible) {
+ alpha(AlphaVisible)
+ } else {
+ alpha(AlphaInvisible)
+ }