summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorDavid Göransson <david.goransson@mullvad.net>2026-04-10 16:15:00 +0200
committerDavid Göransson <david.goransson@mullvad.net>2026-04-10 16:15:00 +0200
commitd01078bcca29284594258d09bf2468a1b18fff68 (patch)
tree876bbb48ab44fb5a99e12d22c0205a677b7cdf76
parent37d615047677f2174af610174f219746a6dc6c34 (diff)
parent6b8f70247f4072fd3404fcdab331726506996e46 (diff)
downloadmullvadvpn-d01078bcca29284594258d09bf2468a1b18fff68.tar.xz
mullvadvpn-d01078bcca29284594258d09bf2468a1b18fff68.zip
Merge branch 'add-search-to-split-tunneling-screen-droid-1732'
-rw-r--r--android/CHANGELOG.md1
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/di/AppModule.kt3
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/di/UiModule.kt21
-rw-r--r--android/lib/feature/account/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/account/impl/AccountScreen.kt2
-rw-r--r--android/lib/feature/anticensorship/impl/src/main/java/net/mullvad/mullvadvpn/feature/anticensorship/impl/AntiCensorshipSettingsScreen.kt4
-rw-r--r--android/lib/feature/anticensorship/impl/src/main/java/net/mullvad/mullvadvpn/feature/anticensorship/impl/selectport/SelectPortScreen.kt2
-rw-r--r--android/lib/feature/apiaccess/impl/src/main/java/net/mullvad/mullvadvpn/feature/apiaccess/impl/screen/detail/ApiAccessMethodDetailsScreen.kt2
-rw-r--r--android/lib/feature/apiaccess/impl/src/main/java/net/mullvad/mullvadvpn/feature/apiaccess/impl/screen/edit/EditApiAccessMethodScreen.kt2
-rw-r--r--android/lib/feature/apiaccess/impl/src/main/java/net/mullvad/mullvadvpn/feature/apiaccess/impl/screen/list/ApiAccessListScreen.kt2
-rw-r--r--android/lib/feature/appearance/impl/src/main/java/net/mullvad/mullvadvpn/feature/appearance/impl/AppearanceScreen.kt2
-rw-r--r--android/lib/feature/appicon/impl/src/main/java/net/mullvad/mullvadvpn/feature/appicon/impl/AppIconScreen.kt2
-rw-r--r--android/lib/feature/appicon/impl/src/main/java/net/mullvad/mullvadvpn/feature/appicon/impl/obfuscation/AppObfuscationRepository.kt5
-rw-r--r--android/lib/feature/appinfo/impl/src/main/java/net/mullvad/mullvadvpn/feature/appinfo/impl/AppInfoScreen.kt2
-rw-r--r--android/lib/feature/appinfo/impl/src/main/java/net/mullvad/mullvadvpn/feature/appinfo/impl/AppInfoViewModel.kt5
-rw-r--r--android/lib/feature/appinfo/impl/src/main/java/net/mullvad/mullvadvpn/feature/appinfo/impl/changelog/ChangelogScreen.kt4
-rw-r--r--android/lib/feature/autoconnect/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/autoconnect/impl/AutoConnectAndLockdownModeScreen.kt2
-rw-r--r--android/lib/feature/customlist/impl/src/main/java/net/mullvad/mullvadvpn/feature/customlist/impl/screen/editlist/EditCustomListScreen.kt2
-rw-r--r--android/lib/feature/customlist/impl/src/main/java/net/mullvad/mullvadvpn/feature/customlist/impl/screen/editlocations/CustomListLocationsScreen.kt4
-rw-r--r--android/lib/feature/customlist/impl/src/main/java/net/mullvad/mullvadvpn/feature/customlist/impl/screen/lists/CustomListsScreen.kt2
-rw-r--r--android/lib/feature/daita/impl/src/main/java/net/mullvad/mullvadvpn/feature/daita/impl/DaitaScreen.kt4
-rw-r--r--android/lib/feature/deleteaccount/impl/src/main/java/net/mullvad/mullvadvpn/feature/deleteaccount/impl/DeleteAccountScreen.kt2
-rw-r--r--android/lib/feature/deleteaccount/impl/src/main/java/net/mullvad/mullvadvpn/feature/deleteaccount/impl/deleteaccountcomplete/DeleteAccountCompleteScreen.kt2
-rw-r--r--android/lib/feature/deleteaccount/impl/src/main/java/net/mullvad/mullvadvpn/feature/deleteaccount/impl/deleteaccountconfirmation/DeleteAccountConfirmationScreen.kt2
-rw-r--r--android/lib/feature/filter/impl/src/main/java/net/mullvad/mullvadvpn/feature/filter/impl/FilterScreen.kt2
-rw-r--r--android/lib/feature/home/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/home/impl/connect/ConnectViewModel.kt5
-rw-r--r--android/lib/feature/home/impl/src/test/kotlin/net/mullvad/mullvadvpn/feature/home/impl/connect/ConnectViewModelTest.kt3
-rw-r--r--android/lib/feature/language/impl/src/main/java/net/mullvad/mullvadvpn/feature/language/impl/LanguageScreen.kt2
-rw-r--r--android/lib/feature/location/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/location/impl/RelayListContent.kt2
-rw-r--r--android/lib/feature/location/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/location/impl/SelectLocationScreen.kt15
-rw-r--r--android/lib/feature/location/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/location/impl/search/SearchLocationScreen.kt63
-rw-r--r--android/lib/feature/managedevices/impl/src/main/java/net/mullvad/mullvadvpn/feature/managedevices/impl/ManageDevicesScreen.kt2
-rw-r--r--android/lib/feature/multihop/impl/src/main/java/net/mullvad/mullvadvpn/feature/multihop/impl/MultihopScreen.kt4
-rw-r--r--android/lib/feature/notification/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/notification/impl/NotificationSettingsScreen.kt2
-rw-r--r--android/lib/feature/problemreport/impl/src/main/java/net/mullvad/mullvadvpn/feature/problemreport/impl/ReportProblemScreen.kt2
-rw-r--r--android/lib/feature/problemreport/impl/src/main/java/net/mullvad/mullvadvpn/feature/problemreport/impl/viewlogs/ViewLogsScreen.kt2
-rw-r--r--android/lib/feature/serveripoverride/impl/src/main/java/net/mullvad/mullvadvpn/feature/serveripoverride/impl/ServerIpOverridesScreen.kt4
-rw-r--r--android/lib/feature/settings/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/settings/impl/SettingsScreen.kt2
-rw-r--r--android/lib/feature/splittunneling/api/src/main/kotlin/net/mullvad/mullvadvpn/feature/splittunneling/api/SearchSplitTunnelingNavKey.kt6
-rw-r--r--android/lib/feature/splittunneling/impl/src/androidTest/kotlin/net/mullvad/mullvadvpn/feature/splittunneling/impl/SplitTunnelingScreenTest.kt19
-rw-r--r--android/lib/feature/splittunneling/impl/src/main/java/net/mullvad/mullvadvpn/feature/splittunneling/impl/SplitTunnelingScreen.kt71
-rw-r--r--android/lib/feature/splittunneling/impl/src/main/java/net/mullvad/mullvadvpn/feature/splittunneling/impl/SplitTunnelingUiState.kt2
-rw-r--r--android/lib/feature/splittunneling/impl/src/main/java/net/mullvad/mullvadvpn/feature/splittunneling/impl/SplitTunnelingUiStatePreviewParameterProvider.kt25
-rw-r--r--android/lib/feature/splittunneling/impl/src/main/java/net/mullvad/mullvadvpn/feature/splittunneling/impl/SplitTunnelingViewModel.kt71
-rw-r--r--android/lib/feature/splittunneling/impl/src/main/java/net/mullvad/mullvadvpn/feature/splittunneling/impl/applist/AppData.kt4
-rw-r--r--android/lib/feature/splittunneling/impl/src/main/java/net/mullvad/mullvadvpn/feature/splittunneling/impl/applist/ApplicationsProvider.kt7
-rw-r--r--android/lib/feature/splittunneling/impl/src/main/java/net/mullvad/mullvadvpn/feature/splittunneling/impl/applist/SplitTunnelingUseCase.kt44
-rw-r--r--android/lib/feature/splittunneling/impl/src/main/java/net/mullvad/mullvadvpn/feature/splittunneling/impl/navigation/SearchSplitTunnelingEntryProvider.kt17
-rw-r--r--android/lib/feature/splittunneling/impl/src/main/java/net/mullvad/mullvadvpn/feature/splittunneling/impl/navigation/SplitTunnelingEntryProvider.kt1
-rw-r--r--android/lib/feature/splittunneling/impl/src/main/java/net/mullvad/mullvadvpn/feature/splittunneling/impl/search/SearchSplitTunnelingScreen.kt203
-rw-r--r--android/lib/feature/splittunneling/impl/src/main/java/net/mullvad/mullvadvpn/feature/splittunneling/impl/search/SearchSplitTunnelingUiState.kt9
-rw-r--r--android/lib/feature/splittunneling/impl/src/main/java/net/mullvad/mullvadvpn/feature/splittunneling/impl/search/SearchSplitTunnelingViewModel.kt63
-rw-r--r--android/lib/feature/splittunneling/impl/src/test/java/net/mullvad/mullvadvpn/feature/splittunneling/impl/SplitTunnelingViewModelTest.kt43
-rw-r--r--android/lib/feature/splittunneling/impl/src/test/java/net/mullvad/mullvadvpn/feature/splittunneling/impl/applist/ApplicationsProviderTest.kt108
-rw-r--r--android/lib/feature/vpnsettings/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/vpnsettings/impl/VpnSettingsScreen.kt4
-rw-r--r--android/lib/grpc/src/main/kotlin/net/mullvad/mullvadvpn/lib/grpc/ManagementService.kt8
-rw-r--r--android/lib/grpc/src/main/kotlin/net/mullvad/mullvadvpn/lib/grpc/mapper/ToDomain.kt4
-rw-r--r--android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/AppId.kt3
-rw-r--r--android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/PackageName.kt3
-rw-r--r--android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/SplitTunnelSettings.kt2
-rw-r--r--android/lib/repository/src/main/kotlin/net/mullvad/mullvadvpn/lib/repository/SplitTunnelingRepository.kt6
-rw-r--r--android/lib/repository/src/main/kotlin/net/mullvad/mullvadvpn/lib/repository/UserPreferencesRepository.kt9
-rw-r--r--android/lib/repository/src/main/proto/user_prefs.proto1
-rw-r--r--android/lib/ui/component/src/main/kotlin/net/mullvad/mullvadvpn/lib/ui/component/MullvadSearchBar.kt67
-rw-r--r--android/lib/ui/component/src/main/kotlin/net/mullvad/mullvadvpn/lib/ui/component/button/NavigateBackButton.kt (renamed from android/lib/ui/component/src/main/kotlin/net/mullvad/mullvadvpn/lib/ui/component/NavigateButton.kt)25
-rw-r--r--android/lib/ui/component/src/main/kotlin/net/mullvad/mullvadvpn/lib/ui/component/button/NavigateCloseButton.kt19
-rw-r--r--android/lib/ui/component/src/main/kotlin/net/mullvad/mullvadvpn/lib/ui/component/button/SearchButton.kt30
-rw-r--r--android/lib/ui/resource/src/main/res/values-ar/strings.xml2
-rw-r--r--android/lib/ui/resource/src/main/res/values-da/strings.xml2
-rw-r--r--android/lib/ui/resource/src/main/res/values-de/strings.xml2
-rw-r--r--android/lib/ui/resource/src/main/res/values-es/strings.xml2
-rw-r--r--android/lib/ui/resource/src/main/res/values-fa/strings.xml2
-rw-r--r--android/lib/ui/resource/src/main/res/values-fi/strings.xml2
-rw-r--r--android/lib/ui/resource/src/main/res/values-fr/strings.xml2
-rw-r--r--android/lib/ui/resource/src/main/res/values-it/strings.xml2
-rw-r--r--android/lib/ui/resource/src/main/res/values-ja/strings.xml2
-rw-r--r--android/lib/ui/resource/src/main/res/values-ko/strings.xml2
-rw-r--r--android/lib/ui/resource/src/main/res/values-my/strings.xml2
-rw-r--r--android/lib/ui/resource/src/main/res/values-nb/strings.xml2
-rw-r--r--android/lib/ui/resource/src/main/res/values-nl/strings.xml2
-rw-r--r--android/lib/ui/resource/src/main/res/values-pl/strings.xml2
-rw-r--r--android/lib/ui/resource/src/main/res/values-pt/strings.xml2
-rw-r--r--android/lib/ui/resource/src/main/res/values-ru/strings.xml2
-rw-r--r--android/lib/ui/resource/src/main/res/values-sv/strings.xml2
-rw-r--r--android/lib/ui/resource/src/main/res/values-th/strings.xml2
-rw-r--r--android/lib/ui/resource/src/main/res/values-tr/strings.xml2
-rw-r--r--android/lib/ui/resource/src/main/res/values-uk/strings.xml2
-rw-r--r--android/lib/ui/resource/src/main/res/values-zh-rCN/strings.xml2
-rw-r--r--android/lib/ui/resource/src/main/res/values-zh-rTW/strings.xml2
-rw-r--r--android/lib/ui/resource/src/main/res/values/strings.xml2
89 files changed, 743 insertions, 366 deletions
diff --git a/android/CHANGELOG.md b/android/CHANGELOG.md
index 70c4f48f15..7d5278a6a5 100644
--- a/android/CHANGELOG.md
+++ b/android/CHANGELOG.md
@@ -26,6 +26,7 @@ Line wrap the file at 100 chars. Th
- Add Ukrainian as a new language in the app.
- Add in-app language selector for Android 13 and up.
- Add reconnect action to tunnel status notification.
+- Add search for Split Tunneling
### Changed
- Drop support for Android 8/8.1 (Android 9/API level 28 or later is now required).
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/di/AppModule.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/di/AppModule.kt
index 942ac4c269..cb8c9de228 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/di/AppModule.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/di/AppModule.kt
@@ -68,8 +68,7 @@ val appModule = module {
single { ScheduleNotificationAlarmUseCase(androidContext(), get()) }
single { AccountExpiryNotificationActionUseCase(get(), get()) }
// TODO Move these back to UiModule when fixDisableBug is removed
- single<String>(named(SELF_PACKAGE_NAME)) { androidContext().packageName }
- single { AppObfuscationRepository(get(), get(named(SELF_PACKAGE_NAME))) }
+ single { AppObfuscationRepository(get(), get()) }
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
single { LanguageRepository(androidContext()) }
}
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 0f63892780..150e16656e 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
@@ -56,11 +56,14 @@ import net.mullvad.mullvadvpn.feature.serveripoverride.impl.reset.ResetServerIpO
import net.mullvad.mullvadvpn.feature.settings.impl.SettingsViewModel
import net.mullvad.mullvadvpn.feature.splittunneling.impl.SplitTunnelingViewModel
import net.mullvad.mullvadvpn.feature.splittunneling.impl.applist.ApplicationsProvider
+import net.mullvad.mullvadvpn.feature.splittunneling.impl.applist.SplitTunnelingUseCase
+import net.mullvad.mullvadvpn.feature.splittunneling.impl.search.SearchSplitTunnelingViewModel
import net.mullvad.mullvadvpn.feature.vpnsettings.impl.VpnSettingsViewModel
import net.mullvad.mullvadvpn.feature.vpnsettings.impl.dns.DnsDialogViewModel
import net.mullvad.mullvadvpn.feature.vpnsettings.impl.mtu.MtuDialogViewModel
import net.mullvad.mullvadvpn.lib.common.constant.BillingTypes
import net.mullvad.mullvadvpn.lib.common.constant.BuildTypes
+import net.mullvad.mullvadvpn.lib.model.PackageName
import net.mullvad.mullvadvpn.lib.model.RelayListType
import net.mullvad.mullvadvpn.lib.payment.PaymentProvider
import net.mullvad.mullvadvpn.lib.repository.ApiAccessRepository
@@ -127,11 +130,8 @@ val uiModule = module {
ComponentName(androidContext(), AutoStartVpnBootCompletedReceiver::class.java)
}
- viewModel { params ->
- SplitTunnelingViewModel(isModal = params.get(), get(), get(), Dispatchers.Default)
- }
-
- single { ApplicationsProvider(get(), get(named(SELF_PACKAGE_NAME))) }
+ single { PackageName(androidContext().packageName) }
+ single { ApplicationsProvider(get(), get()) }
scope<MainActivity> { scoped { ServiceConnectionManager(androidContext()) } }
single { InetAddressValidator.getInstance() }
single { androidContext().assets }
@@ -155,6 +155,7 @@ val uiModule = module {
single { RelayListFilterRepository(get()) }
single { VoucherRepository(get(), get()) }
single { SplitTunnelingRepository(get()) }
+ single { SplitTunnelingUseCase(get(), get(), get()) }
single { ApiAccessRepository(get()) }
single { NewDeviceRepository() }
single { SplashCompleteRepository() }
@@ -266,7 +267,7 @@ val uiModule = module {
resources = get(),
isPlayBuild = IS_PLAY_BUILD,
isFdroidBuild = IS_FDROID_BUILD,
- packageName = get(named(SELF_PACKAGE_NAME)),
+ self = get(),
)
}
viewModel {
@@ -286,7 +287,7 @@ val uiModule = module {
resources = get(),
isPlayBuild = IS_PLAY_BUILD,
isFdroidBuild = IS_FDROID_BUILD,
- packageName = get(named(SELF_PACKAGE_NAME)),
+ self = get(),
)
}
viewModel { params -> DeviceListViewModel(accountNumber = params.get(), get()) }
@@ -419,13 +420,17 @@ val uiModule = module {
viewModel { LanguageViewModel(get()) }
}
viewModel { AutoConnectAndLockdownModeViewModel(isPlayBuild = IS_PLAY_BUILD) }
+ viewModel { params ->
+ SplitTunnelingViewModel(isModal = params.get(), get(), get(), get(), Dispatchers.Default)
+ }
+
+ viewModel { SearchSplitTunnelingViewModel(get(), get(), Dispatchers.Default) }
// This view model must be single so we correctly attach lifecycle and share it with activity
single { MullvadAppViewModel(get(), get()) }
}
const val APP_PREFERENCES_NAME = "${BuildConfig.APPLICATION_ID}.app_preferences"
-const val SELF_PACKAGE_NAME = "SELF_PACKAGE_NAME"
const val KERMIT_FILE_LOG_DIR_NAME = "android_app_logs"
private const val BOOT_COMPLETED_RECEIVER_COMPONENT_NAME = "BOOT_COMPLETED_RECEIVER_COMPONENT_NAME"
diff --git a/android/lib/feature/account/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/account/impl/AccountScreen.kt b/android/lib/feature/account/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/account/impl/AccountScreen.kt
index 794f7ada8c..2ae962c589 100644
--- a/android/lib/feature/account/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/account/impl/AccountScreen.kt
+++ b/android/lib/feature/account/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/account/impl/AccountScreen.kt
@@ -55,8 +55,8 @@ import net.mullvad.mullvadvpn.feature.login.api.LoginNavKey
import net.mullvad.mullvadvpn.feature.managedevices.api.ManageDevicesNavKey
import net.mullvad.mullvadvpn.lib.common.Lc
import net.mullvad.mullvadvpn.lib.common.util.toExpiryDateString
-import net.mullvad.mullvadvpn.lib.ui.component.NavigateCloseIconButton
import net.mullvad.mullvadvpn.lib.ui.component.ScaffoldWithMediumTopBar
+import net.mullvad.mullvadvpn.lib.ui.component.button.NavigateCloseIconButton
import net.mullvad.mullvadvpn.lib.ui.designsystem.NegativeOutlinedButton
import net.mullvad.mullvadvpn.lib.ui.designsystem.PrimaryTextButton
import net.mullvad.mullvadvpn.lib.ui.tag.MANAGE_DEVICES_BUTTON_TEST_TAG
diff --git a/android/lib/feature/anticensorship/impl/src/main/java/net/mullvad/mullvadvpn/feature/anticensorship/impl/AntiCensorshipSettingsScreen.kt b/android/lib/feature/anticensorship/impl/src/main/java/net/mullvad/mullvadvpn/feature/anticensorship/impl/AntiCensorshipSettingsScreen.kt
index cb853f5bc5..622564bcfd 100644
--- a/android/lib/feature/anticensorship/impl/src/main/java/net/mullvad/mullvadvpn/feature/anticensorship/impl/AntiCensorshipSettingsScreen.kt
+++ b/android/lib/feature/anticensorship/impl/src/main/java/net/mullvad/mullvadvpn/feature/anticensorship/impl/AntiCensorshipSettingsScreen.kt
@@ -29,10 +29,10 @@ import net.mullvad.mullvadvpn.feature.anticensorship.api.SelectPortNavKey
import net.mullvad.mullvadvpn.lib.common.Lc
import net.mullvad.mullvadvpn.lib.model.ObfuscationMode
import net.mullvad.mullvadvpn.lib.model.PortType
-import net.mullvad.mullvadvpn.lib.ui.component.NavigateBackIconButton
-import net.mullvad.mullvadvpn.lib.ui.component.NavigateCloseIconButton
import net.mullvad.mullvadvpn.lib.ui.component.ScaffoldWithMediumTopBar
import net.mullvad.mullvadvpn.lib.ui.component.annotatedStringResource
+import net.mullvad.mullvadvpn.lib.ui.component.button.NavigateBackIconButton
+import net.mullvad.mullvadvpn.lib.ui.component.button.NavigateCloseIconButton
import net.mullvad.mullvadvpn.lib.ui.component.listitem.InfoListItem
import net.mullvad.mullvadvpn.lib.ui.component.listitem.ObfuscationModeListItem
import net.mullvad.mullvadvpn.lib.ui.component.listitem.SelectableListItem
diff --git a/android/lib/feature/anticensorship/impl/src/main/java/net/mullvad/mullvadvpn/feature/anticensorship/impl/selectport/SelectPortScreen.kt b/android/lib/feature/anticensorship/impl/src/main/java/net/mullvad/mullvadvpn/feature/anticensorship/impl/selectport/SelectPortScreen.kt
index 3ea03b0984..01bb6ec73b 100644
--- a/android/lib/feature/anticensorship/impl/src/main/java/net/mullvad/mullvadvpn/feature/anticensorship/impl/selectport/SelectPortScreen.kt
+++ b/android/lib/feature/anticensorship/impl/src/main/java/net/mullvad/mullvadvpn/feature/anticensorship/impl/selectport/SelectPortScreen.kt
@@ -23,8 +23,8 @@ import net.mullvad.mullvadvpn.feature.anticensorship.api.SelectPortNavKey
import net.mullvad.mullvadvpn.lib.common.Lc
import net.mullvad.mullvadvpn.lib.model.Constraint
import net.mullvad.mullvadvpn.lib.model.Port
-import net.mullvad.mullvadvpn.lib.ui.component.NavigateBackIconButton
import net.mullvad.mullvadvpn.lib.ui.component.ScaffoldWithMediumTopBar
+import net.mullvad.mullvadvpn.lib.ui.component.button.NavigateBackIconButton
import net.mullvad.mullvadvpn.lib.ui.component.listitem.CustomPortListItem
import net.mullvad.mullvadvpn.lib.ui.component.listitem.InfoListItem
import net.mullvad.mullvadvpn.lib.ui.component.listitem.SelectableListItem
diff --git a/android/lib/feature/apiaccess/impl/src/main/java/net/mullvad/mullvadvpn/feature/apiaccess/impl/screen/detail/ApiAccessMethodDetailsScreen.kt b/android/lib/feature/apiaccess/impl/src/main/java/net/mullvad/mullvadvpn/feature/apiaccess/impl/screen/detail/ApiAccessMethodDetailsScreen.kt
index 60aea74567..439cc534f4 100644
--- a/android/lib/feature/apiaccess/impl/src/main/java/net/mullvad/mullvadvpn/feature/apiaccess/impl/screen/detail/ApiAccessMethodDetailsScreen.kt
+++ b/android/lib/feature/apiaccess/impl/src/main/java/net/mullvad/mullvadvpn/feature/apiaccess/impl/screen/detail/ApiAccessMethodDetailsScreen.kt
@@ -48,8 +48,8 @@ import net.mullvad.mullvadvpn.feature.apiaccess.impl.component.TestMethodButton
import net.mullvad.mullvadvpn.feature.apiaccess.impl.util.toDisplayName
import net.mullvad.mullvadvpn.lib.model.ApiAccessMethod
import net.mullvad.mullvadvpn.lib.model.ApiAccessMethodId
-import net.mullvad.mullvadvpn.lib.ui.component.NavigateBackIconButton
import net.mullvad.mullvadvpn.lib.ui.component.ScaffoldWithMediumTopBar
+import net.mullvad.mullvadvpn.lib.ui.component.button.NavigateBackIconButton
import net.mullvad.mullvadvpn.lib.ui.component.listitem.NavigationListItem
import net.mullvad.mullvadvpn.lib.ui.component.listitem.SwitchListItem
import net.mullvad.mullvadvpn.lib.ui.component.text.ListItemInfo
diff --git a/android/lib/feature/apiaccess/impl/src/main/java/net/mullvad/mullvadvpn/feature/apiaccess/impl/screen/edit/EditApiAccessMethodScreen.kt b/android/lib/feature/apiaccess/impl/src/main/java/net/mullvad/mullvadvpn/feature/apiaccess/impl/screen/edit/EditApiAccessMethodScreen.kt
index b8bc7f83d2..59888c0528 100644
--- a/android/lib/feature/apiaccess/impl/src/main/java/net/mullvad/mullvadvpn/feature/apiaccess/impl/screen/edit/EditApiAccessMethodScreen.kt
+++ b/android/lib/feature/apiaccess/impl/src/main/java/net/mullvad/mullvadvpn/feature/apiaccess/impl/screen/edit/EditApiAccessMethodScreen.kt
@@ -64,8 +64,8 @@ import net.mullvad.mullvadvpn.lib.model.ApiAccessMethodId
import net.mullvad.mullvadvpn.lib.model.ApiAccessMethodName
import net.mullvad.mullvadvpn.lib.model.Cipher
import net.mullvad.mullvadvpn.lib.model.InvalidDataError
-import net.mullvad.mullvadvpn.lib.ui.component.NavigateCloseIconButton
import net.mullvad.mullvadvpn.lib.ui.component.ScaffoldWithSmallTopBar
+import net.mullvad.mullvadvpn.lib.ui.component.button.NavigateCloseIconButton
import net.mullvad.mullvadvpn.lib.ui.component.drawVerticalScrollbar
import net.mullvad.mullvadvpn.lib.ui.component.textfield.ErrorSupportingText
import net.mullvad.mullvadvpn.lib.ui.component.textfield.mullvadDarkTextFieldColors
diff --git a/android/lib/feature/apiaccess/impl/src/main/java/net/mullvad/mullvadvpn/feature/apiaccess/impl/screen/list/ApiAccessListScreen.kt b/android/lib/feature/apiaccess/impl/src/main/java/net/mullvad/mullvadvpn/feature/apiaccess/impl/screen/list/ApiAccessListScreen.kt
index d06ca73d7d..403a8e57a2 100644
--- a/android/lib/feature/apiaccess/impl/src/main/java/net/mullvad/mullvadvpn/feature/apiaccess/impl/screen/list/ApiAccessListScreen.kt
+++ b/android/lib/feature/apiaccess/impl/src/main/java/net/mullvad/mullvadvpn/feature/apiaccess/impl/screen/list/ApiAccessListScreen.kt
@@ -29,8 +29,8 @@ import net.mullvad.mullvadvpn.feature.apiaccess.api.ApiAccessMethodInfoNavKey
import net.mullvad.mullvadvpn.feature.apiaccess.api.EditApiAccessMethodNavKey
import net.mullvad.mullvadvpn.feature.apiaccess.impl.util.toDisplayName
import net.mullvad.mullvadvpn.lib.model.ApiAccessMethodSetting
-import net.mullvad.mullvadvpn.lib.ui.component.NavigateBackIconButton
import net.mullvad.mullvadvpn.lib.ui.component.ScaffoldWithMediumTopBar
+import net.mullvad.mullvadvpn.lib.ui.component.button.NavigateBackIconButton
import net.mullvad.mullvadvpn.lib.ui.component.listitem.NavigationListItem
import net.mullvad.mullvadvpn.lib.ui.component.positionForIndex
import net.mullvad.mullvadvpn.lib.ui.component.text.ScreenDescription
diff --git a/android/lib/feature/appearance/impl/src/main/java/net/mullvad/mullvadvpn/feature/appearance/impl/AppearanceScreen.kt b/android/lib/feature/appearance/impl/src/main/java/net/mullvad/mullvadvpn/feature/appearance/impl/AppearanceScreen.kt
index ed9304b45d..a2436b5582 100644
--- a/android/lib/feature/appearance/impl/src/main/java/net/mullvad/mullvadvpn/feature/appearance/impl/AppearanceScreen.kt
+++ b/android/lib/feature/appearance/impl/src/main/java/net/mullvad/mullvadvpn/feature/appearance/impl/AppearanceScreen.kt
@@ -14,8 +14,8 @@ import net.mullvad.mullvadvpn.common.compose.unlessIsDetail
import net.mullvad.mullvadvpn.core.Navigator
import net.mullvad.mullvadvpn.feature.appicon.api.AppIconNavKey
import net.mullvad.mullvadvpn.feature.language.api.LanguageNavKey
-import net.mullvad.mullvadvpn.lib.ui.component.NavigateBackIconButton
import net.mullvad.mullvadvpn.lib.ui.component.ScaffoldWithMediumTopBar
+import net.mullvad.mullvadvpn.lib.ui.component.button.NavigateBackIconButton
import net.mullvad.mullvadvpn.lib.ui.component.listitem.NavigationListItem
import net.mullvad.mullvadvpn.lib.ui.designsystem.Position
import net.mullvad.mullvadvpn.lib.ui.resource.R
diff --git a/android/lib/feature/appicon/impl/src/main/java/net/mullvad/mullvadvpn/feature/appicon/impl/AppIconScreen.kt b/android/lib/feature/appicon/impl/src/main/java/net/mullvad/mullvadvpn/feature/appicon/impl/AppIconScreen.kt
index e002cf15dd..02083c362d 100644
--- a/android/lib/feature/appicon/impl/src/main/java/net/mullvad/mullvadvpn/feature/appicon/impl/AppIconScreen.kt
+++ b/android/lib/feature/appicon/impl/src/main/java/net/mullvad/mullvadvpn/feature/appicon/impl/AppIconScreen.kt
@@ -35,10 +35,10 @@ import net.mullvad.mullvadvpn.common.compose.unlessIsDetail
import net.mullvad.mullvadvpn.core.Navigator
import net.mullvad.mullvadvpn.feature.appicon.impl.obfuscation.AppObfuscation
import net.mullvad.mullvadvpn.lib.common.Lc
-import net.mullvad.mullvadvpn.lib.ui.component.NavigateBackIconButton
import net.mullvad.mullvadvpn.lib.ui.component.SPACE_CHAR
import net.mullvad.mullvadvpn.lib.ui.component.ScaffoldWithMediumTopBar
import net.mullvad.mullvadvpn.lib.ui.component.annotatedStringResource
+import net.mullvad.mullvadvpn.lib.ui.component.button.NavigateBackIconButton
import net.mullvad.mullvadvpn.lib.ui.component.griditem.AppIconAndTitleGridItem
import net.mullvad.mullvadvpn.lib.ui.component.text.ScreenDescription
import net.mullvad.mullvadvpn.lib.ui.designsystem.ListHeader
diff --git a/android/lib/feature/appicon/impl/src/main/java/net/mullvad/mullvadvpn/feature/appicon/impl/obfuscation/AppObfuscationRepository.kt b/android/lib/feature/appicon/impl/src/main/java/net/mullvad/mullvadvpn/feature/appicon/impl/obfuscation/AppObfuscationRepository.kt
index e4a7abe68f..e146f5d551 100644
--- a/android/lib/feature/appicon/impl/src/main/java/net/mullvad/mullvadvpn/feature/appicon/impl/obfuscation/AppObfuscationRepository.kt
+++ b/android/lib/feature/appicon/impl/src/main/java/net/mullvad/mullvadvpn/feature/appicon/impl/obfuscation/AppObfuscationRepository.kt
@@ -10,11 +10,12 @@ import android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED
import android.content.pm.PackageManager.DONT_KILL_APP
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
+import net.mullvad.mullvadvpn.lib.model.PackageName
import net.mullvad.mullvadvpn.lib.ui.resource.R
class AppObfuscationRepository(
private val packageManager: PackageManager,
- private val packageName: String,
+ private val self: PackageName,
) {
private val _currentAppObfuscation = MutableStateFlow(getObfuscation())
val currentAppObfuscation: StateFlow<AppObfuscation> = _currentAppObfuscation
@@ -54,7 +55,7 @@ class AppObfuscationRepository(
else -> error("Unknown component enabled setting")
}
- private fun AppObfuscation.toComponentName() = ComponentName(packageName, className)
+ private fun AppObfuscation.toComponentName() = ComponentName(self.value, className)
}
/**
diff --git a/android/lib/feature/appinfo/impl/src/main/java/net/mullvad/mullvadvpn/feature/appinfo/impl/AppInfoScreen.kt b/android/lib/feature/appinfo/impl/src/main/java/net/mullvad/mullvadvpn/feature/appinfo/impl/AppInfoScreen.kt
index 5414d35ebd..fbca756979 100644
--- a/android/lib/feature/appinfo/impl/src/main/java/net/mullvad/mullvadvpn/feature/appinfo/impl/AppInfoScreen.kt
+++ b/android/lib/feature/appinfo/impl/src/main/java/net/mullvad/mullvadvpn/feature/appinfo/impl/AppInfoScreen.kt
@@ -28,8 +28,8 @@ import net.mullvad.mullvadvpn.common.compose.unlessIsDetail
import net.mullvad.mullvadvpn.core.Navigator
import net.mullvad.mullvadvpn.feature.appinfo.api.ChangelogNavKey
import net.mullvad.mullvadvpn.lib.common.Lc
-import net.mullvad.mullvadvpn.lib.ui.component.NavigateBackIconButton
import net.mullvad.mullvadvpn.lib.ui.component.ScaffoldWithMediumTopBar
+import net.mullvad.mullvadvpn.lib.ui.component.button.NavigateBackIconButton
import net.mullvad.mullvadvpn.lib.ui.component.listitem.ExternalLinkListItem
import net.mullvad.mullvadvpn.lib.ui.component.listitem.NavigationListItem
import net.mullvad.mullvadvpn.lib.ui.designsystem.MullvadCircularProgressIndicatorLarge
diff --git a/android/lib/feature/appinfo/impl/src/main/java/net/mullvad/mullvadvpn/feature/appinfo/impl/AppInfoViewModel.kt b/android/lib/feature/appinfo/impl/src/main/java/net/mullvad/mullvadvpn/feature/appinfo/impl/AppInfoViewModel.kt
index d5a59a2d56..7c39a1d9f8 100644
--- a/android/lib/feature/appinfo/impl/src/main/java/net/mullvad/mullvadvpn/feature/appinfo/impl/AppInfoViewModel.kt
+++ b/android/lib/feature/appinfo/impl/src/main/java/net/mullvad/mullvadvpn/feature/appinfo/impl/AppInfoViewModel.kt
@@ -14,6 +14,7 @@ import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import net.mullvad.mullvadvpn.lib.common.Lc
import net.mullvad.mullvadvpn.lib.common.constant.VIEW_MODEL_STOP_TIMEOUT
+import net.mullvad.mullvadvpn.lib.model.PackageName
import net.mullvad.mullvadvpn.lib.repository.AppVersionInfoRepository
import net.mullvad.mullvadvpn.lib.ui.resource.R
@@ -22,7 +23,7 @@ class AppInfoViewModel(
private val resources: Resources,
private val isPlayBuild: Boolean,
private val isFdroidBuild: Boolean,
- private val packageName: String,
+ private val self: PackageName,
) : ViewModel() {
private val _uiSideEffect = Channel<AppInfoSideEffect>()
@@ -41,7 +42,7 @@ class AppInfoViewModel(
val sideEffect =
if (isPlayBuild || isFdroidBuild) {
AppInfoSideEffect.OpenUri(
- uri = resources.getString(R.string.market_uri, packageName).toUri(),
+ uri = resources.getString(R.string.market_uri, self.value).toUri(),
errorMessage = resources.getString(R.string.uri_market_app_not_found),
)
} else {
diff --git a/android/lib/feature/appinfo/impl/src/main/java/net/mullvad/mullvadvpn/feature/appinfo/impl/changelog/ChangelogScreen.kt b/android/lib/feature/appinfo/impl/src/main/java/net/mullvad/mullvadvpn/feature/appinfo/impl/changelog/ChangelogScreen.kt
index c755ea86ca..7bf1a2619e 100644
--- a/android/lib/feature/appinfo/impl/src/main/java/net/mullvad/mullvadvpn/feature/appinfo/impl/changelog/ChangelogScreen.kt
+++ b/android/lib/feature/appinfo/impl/src/main/java/net/mullvad/mullvadvpn/feature/appinfo/impl/changelog/ChangelogScreen.kt
@@ -25,9 +25,9 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.compose.dropUnlessResumed
import net.mullvad.mullvadvpn.core.Navigator
import net.mullvad.mullvadvpn.feature.appinfo.api.ChangelogNavKey
-import net.mullvad.mullvadvpn.lib.ui.component.NavigateBackIconButton
-import net.mullvad.mullvadvpn.lib.ui.component.NavigateCloseIconButton
import net.mullvad.mullvadvpn.lib.ui.component.ScaffoldWithMediumTopBar
+import net.mullvad.mullvadvpn.lib.ui.component.button.NavigateBackIconButton
+import net.mullvad.mullvadvpn.lib.ui.component.button.NavigateCloseIconButton
import net.mullvad.mullvadvpn.lib.ui.component.drawVerticalScrollbar
import net.mullvad.mullvadvpn.lib.ui.resource.R
import net.mullvad.mullvadvpn.lib.ui.theme.AppTheme
diff --git a/android/lib/feature/autoconnect/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/autoconnect/impl/AutoConnectAndLockdownModeScreen.kt b/android/lib/feature/autoconnect/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/autoconnect/impl/AutoConnectAndLockdownModeScreen.kt
index 568e41ef2d..db9f9df50b 100644
--- a/android/lib/feature/autoconnect/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/autoconnect/impl/AutoConnectAndLockdownModeScreen.kt
+++ b/android/lib/feature/autoconnect/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/autoconnect/impl/AutoConnectAndLockdownModeScreen.kt
@@ -62,8 +62,8 @@ import net.mullvad.mullvadvpn.core.Navigator
import net.mullvad.mullvadvpn.feature.autoconnect.impl.PAGES.Companion.annotatedTopText
import net.mullvad.mullvadvpn.lib.common.util.appendHideNavOnPlayBuild
import net.mullvad.mullvadvpn.lib.common.util.openVpnSettings
-import net.mullvad.mullvadvpn.lib.ui.component.NavigateBackIconButton
import net.mullvad.mullvadvpn.lib.ui.component.ScaffoldWithMediumTopBar
+import net.mullvad.mullvadvpn.lib.ui.component.button.NavigateBackIconButton
import net.mullvad.mullvadvpn.lib.ui.component.toAnnotatedString
import net.mullvad.mullvadvpn.lib.ui.designsystem.PrimaryButton
import net.mullvad.mullvadvpn.lib.ui.resource.R
diff --git a/android/lib/feature/customlist/impl/src/main/java/net/mullvad/mullvadvpn/feature/customlist/impl/screen/editlist/EditCustomListScreen.kt b/android/lib/feature/customlist/impl/src/main/java/net/mullvad/mullvadvpn/feature/customlist/impl/screen/editlist/EditCustomListScreen.kt
index cb81e94942..34caf67719 100644
--- a/android/lib/feature/customlist/impl/src/main/java/net/mullvad/mullvadvpn/feature/customlist/impl/screen/editlist/EditCustomListScreen.kt
+++ b/android/lib/feature/customlist/impl/src/main/java/net/mullvad/mullvadvpn/feature/customlist/impl/screen/editlist/EditCustomListScreen.kt
@@ -37,8 +37,8 @@ import net.mullvad.mullvadvpn.feature.customlist.api.EditCustomListNameNavKey
import net.mullvad.mullvadvpn.feature.customlist.api.EditCustomListNavResult
import net.mullvad.mullvadvpn.lib.model.CustomListId
import net.mullvad.mullvadvpn.lib.model.CustomListName
-import net.mullvad.mullvadvpn.lib.ui.component.NavigateBackIconButton
import net.mullvad.mullvadvpn.lib.ui.component.ScaffoldWithMediumTopBar
+import net.mullvad.mullvadvpn.lib.ui.component.button.NavigateBackIconButton
import net.mullvad.mullvadvpn.lib.ui.component.listitem.EditCustomListListItem
import net.mullvad.mullvadvpn.lib.ui.designsystem.MullvadCircularProgressIndicatorLarge
import net.mullvad.mullvadvpn.lib.ui.designsystem.Position
diff --git a/android/lib/feature/customlist/impl/src/main/java/net/mullvad/mullvadvpn/feature/customlist/impl/screen/editlocations/CustomListLocationsScreen.kt b/android/lib/feature/customlist/impl/src/main/java/net/mullvad/mullvadvpn/feature/customlist/impl/screen/editlocations/CustomListLocationsScreen.kt
index 089a81f84c..18d2c66156 100644
--- a/android/lib/feature/customlist/impl/src/main/java/net/mullvad/mullvadvpn/feature/customlist/impl/screen/editlocations/CustomListLocationsScreen.kt
+++ b/android/lib/feature/customlist/impl/src/main/java/net/mullvad/mullvadvpn/feature/customlist/impl/screen/editlocations/CustomListLocationsScreen.kt
@@ -39,8 +39,8 @@ import net.mullvad.mullvadvpn.feature.customlist.api.EditCustomListNavResult
import net.mullvad.mullvadvpn.feature.customlist.impl.screen.lists.ContentType
import net.mullvad.mullvadvpn.lib.common.Lce
import net.mullvad.mullvadvpn.lib.model.RelayItem
-import net.mullvad.mullvadvpn.lib.ui.component.NavigateBackIconButton
import net.mullvad.mullvadvpn.lib.ui.component.ScaffoldWithSmallTopBar
+import net.mullvad.mullvadvpn.lib.ui.component.button.NavigateBackIconButton
import net.mullvad.mullvadvpn.lib.ui.component.drawVerticalScrollbar
import net.mullvad.mullvadvpn.lib.ui.component.relaylist.CheckableRelayListItem
import net.mullvad.mullvadvpn.lib.ui.designsystem.MullvadCircularProgressIndicatorLarge
@@ -239,7 +239,7 @@ private fun LazyListScope.content(
@Composable
private fun LocationsEmptyText(searchTerm: String) {
Text(
- text = stringResource(R.string.search_location_empty_text, searchTerm),
+ text = stringResource(R.string.search_no_matches_for_text, searchTerm),
style = MaterialTheme.typography.bodyMedium,
textAlign = TextAlign.Center,
color = MaterialTheme.colorScheme.onSurfaceVariant,
diff --git a/android/lib/feature/customlist/impl/src/main/java/net/mullvad/mullvadvpn/feature/customlist/impl/screen/lists/CustomListsScreen.kt b/android/lib/feature/customlist/impl/src/main/java/net/mullvad/mullvadvpn/feature/customlist/impl/screen/lists/CustomListsScreen.kt
index 83a74c9bba..e8e770a4eb 100644
--- a/android/lib/feature/customlist/impl/src/main/java/net/mullvad/mullvadvpn/feature/customlist/impl/screen/lists/CustomListsScreen.kt
+++ b/android/lib/feature/customlist/impl/src/main/java/net/mullvad/mullvadvpn/feature/customlist/impl/screen/lists/CustomListsScreen.kt
@@ -36,8 +36,8 @@ import net.mullvad.mullvadvpn.feature.customlist.api.EditCustomListNavKey
import net.mullvad.mullvadvpn.feature.customlist.api.EditCustomListNavResult
import net.mullvad.mullvadvpn.lib.model.CustomList
import net.mullvad.mullvadvpn.lib.model.communication.CustomListActionResultData
-import net.mullvad.mullvadvpn.lib.ui.component.NavigateBackIconButton
import net.mullvad.mullvadvpn.lib.ui.component.ScaffoldWithMediumTopBar
+import net.mullvad.mullvadvpn.lib.ui.component.button.NavigateBackIconButton
import net.mullvad.mullvadvpn.lib.ui.component.listitem.NavigationListItem
import net.mullvad.mullvadvpn.lib.ui.component.positionForIndex
import net.mullvad.mullvadvpn.lib.ui.designsystem.MullvadCircularProgressIndicatorLarge
diff --git a/android/lib/feature/daita/impl/src/main/java/net/mullvad/mullvadvpn/feature/daita/impl/DaitaScreen.kt b/android/lib/feature/daita/impl/src/main/java/net/mullvad/mullvadvpn/feature/daita/impl/DaitaScreen.kt
index fc5cb4cdc5..d698b26f09 100644
--- a/android/lib/feature/daita/impl/src/main/java/net/mullvad/mullvadvpn/feature/daita/impl/DaitaScreen.kt
+++ b/android/lib/feature/daita/impl/src/main/java/net/mullvad/mullvadvpn/feature/daita/impl/DaitaScreen.kt
@@ -40,9 +40,9 @@ import net.mullvad.mullvadvpn.feature.daita.api.DaitaDirectOnlyConfirmedNavResul
import net.mullvad.mullvadvpn.feature.daita.api.DaitaDirectOnlyInfoNavKey
import net.mullvad.mullvadvpn.lib.common.Lc
import net.mullvad.mullvadvpn.lib.model.FeatureIndicator
-import net.mullvad.mullvadvpn.lib.ui.component.NavigateBackIconButton
-import net.mullvad.mullvadvpn.lib.ui.component.NavigateCloseIconButton
import net.mullvad.mullvadvpn.lib.ui.component.ScaffoldWithMediumTopBar
+import net.mullvad.mullvadvpn.lib.ui.component.button.NavigateBackIconButton
+import net.mullvad.mullvadvpn.lib.ui.component.button.NavigateCloseIconButton
import net.mullvad.mullvadvpn.lib.ui.component.listitem.SwitchListItem
import net.mullvad.mullvadvpn.lib.ui.component.text.ScreenDescription
import net.mullvad.mullvadvpn.lib.ui.designsystem.MullvadCircularProgressIndicatorLarge
diff --git a/android/lib/feature/deleteaccount/impl/src/main/java/net/mullvad/mullvadvpn/feature/deleteaccount/impl/DeleteAccountScreen.kt b/android/lib/feature/deleteaccount/impl/src/main/java/net/mullvad/mullvadvpn/feature/deleteaccount/impl/DeleteAccountScreen.kt
index d2ca63c9ca..9700b41b27 100644
--- a/android/lib/feature/deleteaccount/impl/src/main/java/net/mullvad/mullvadvpn/feature/deleteaccount/impl/DeleteAccountScreen.kt
+++ b/android/lib/feature/deleteaccount/impl/src/main/java/net/mullvad/mullvadvpn/feature/deleteaccount/impl/DeleteAccountScreen.kt
@@ -30,9 +30,9 @@ import androidx.lifecycle.compose.dropUnlessResumed
import net.mullvad.mullvadvpn.core.Navigator
import net.mullvad.mullvadvpn.feature.deleteaccount.api.DeleteAccountConfirmationNavKey
import net.mullvad.mullvadvpn.lib.ui.component.CheckboxConfirmation
-import net.mullvad.mullvadvpn.lib.ui.component.NavigateBackIconButton
import net.mullvad.mullvadvpn.lib.ui.component.ScaffoldWithMediumTopBar
import net.mullvad.mullvadvpn.lib.ui.component.annotatedStringResource
+import net.mullvad.mullvadvpn.lib.ui.component.button.NavigateBackIconButton
import net.mullvad.mullvadvpn.lib.ui.designsystem.PrimaryButton
import net.mullvad.mullvadvpn.lib.ui.resource.R
import net.mullvad.mullvadvpn.lib.ui.theme.AppTheme
diff --git a/android/lib/feature/deleteaccount/impl/src/main/java/net/mullvad/mullvadvpn/feature/deleteaccount/impl/deleteaccountcomplete/DeleteAccountCompleteScreen.kt b/android/lib/feature/deleteaccount/impl/src/main/java/net/mullvad/mullvadvpn/feature/deleteaccount/impl/deleteaccountcomplete/DeleteAccountCompleteScreen.kt
index 65ecd72cdb..009c46e8bb 100644
--- a/android/lib/feature/deleteaccount/impl/src/main/java/net/mullvad/mullvadvpn/feature/deleteaccount/impl/deleteaccountcomplete/DeleteAccountCompleteScreen.kt
+++ b/android/lib/feature/deleteaccount/impl/src/main/java/net/mullvad/mullvadvpn/feature/deleteaccount/impl/deleteaccountcomplete/DeleteAccountCompleteScreen.kt
@@ -25,8 +25,8 @@ import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import net.mullvad.mullvadvpn.core.Navigator
import net.mullvad.mullvadvpn.feature.login.api.LoginNavKey
-import net.mullvad.mullvadvpn.lib.ui.component.NavigateCloseIconButton
import net.mullvad.mullvadvpn.lib.ui.component.ScaffoldWithSmallTopBar
+import net.mullvad.mullvadvpn.lib.ui.component.button.NavigateCloseIconButton
import net.mullvad.mullvadvpn.lib.ui.designsystem.PrimaryButton
import net.mullvad.mullvadvpn.lib.ui.resource.R
import net.mullvad.mullvadvpn.lib.ui.theme.AppTheme
diff --git a/android/lib/feature/deleteaccount/impl/src/main/java/net/mullvad/mullvadvpn/feature/deleteaccount/impl/deleteaccountconfirmation/DeleteAccountConfirmationScreen.kt b/android/lib/feature/deleteaccount/impl/src/main/java/net/mullvad/mullvadvpn/feature/deleteaccount/impl/deleteaccountconfirmation/DeleteAccountConfirmationScreen.kt
index bbe93a88de..b8c9b91c21 100644
--- a/android/lib/feature/deleteaccount/impl/src/main/java/net/mullvad/mullvadvpn/feature/deleteaccount/impl/deleteaccountconfirmation/DeleteAccountConfirmationScreen.kt
+++ b/android/lib/feature/deleteaccount/impl/src/main/java/net/mullvad/mullvadvpn/feature/deleteaccount/impl/deleteaccountconfirmation/DeleteAccountConfirmationScreen.kt
@@ -62,9 +62,9 @@ import net.mullvad.mullvadvpn.common.compose.showSnackbarImmediately
import net.mullvad.mullvadvpn.core.Navigator
import net.mullvad.mullvadvpn.feature.deleteaccount.api.DeleteAccountCompleteNavKey
import net.mullvad.mullvadvpn.lib.model.DeleteAccountError
-import net.mullvad.mullvadvpn.lib.ui.component.NavigateBackIconButton
import net.mullvad.mullvadvpn.lib.ui.component.ScaffoldWithMediumTopBar
import net.mullvad.mullvadvpn.lib.ui.component.annotatedStringResource
+import net.mullvad.mullvadvpn.lib.ui.component.button.NavigateBackIconButton
import net.mullvad.mullvadvpn.lib.ui.component.textfield.mullvadDarkTextFieldColors
import net.mullvad.mullvadvpn.lib.ui.designsystem.MullvadCircularProgressIndicatorSmall
import net.mullvad.mullvadvpn.lib.ui.designsystem.NegativeButton
diff --git a/android/lib/feature/filter/impl/src/main/java/net/mullvad/mullvadvpn/feature/filter/impl/FilterScreen.kt b/android/lib/feature/filter/impl/src/main/java/net/mullvad/mullvadvpn/feature/filter/impl/FilterScreen.kt
index 5d6e99e6b1..bfcb600ea7 100644
--- a/android/lib/feature/filter/impl/src/main/java/net/mullvad/mullvadvpn/feature/filter/impl/FilterScreen.kt
+++ b/android/lib/feature/filter/impl/src/main/java/net/mullvad/mullvadvpn/feature/filter/impl/FilterScreen.kt
@@ -37,8 +37,8 @@ import net.mullvad.mullvadvpn.lib.model.Constraint
import net.mullvad.mullvadvpn.lib.model.Ownership
import net.mullvad.mullvadvpn.lib.model.ProviderId
import net.mullvad.mullvadvpn.lib.model.Providers
-import net.mullvad.mullvadvpn.lib.ui.component.NavigateBackIconButton
import net.mullvad.mullvadvpn.lib.ui.component.ScaffoldWithSmallTopBar
+import net.mullvad.mullvadvpn.lib.ui.component.button.NavigateBackIconButton
import net.mullvad.mullvadvpn.lib.ui.component.listitem.CheckableListItem
import net.mullvad.mullvadvpn.lib.ui.component.listitem.ExpandableListItem
import net.mullvad.mullvadvpn.lib.ui.component.listitem.SelectableListItem
diff --git a/android/lib/feature/home/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/home/impl/connect/ConnectViewModel.kt b/android/lib/feature/home/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/home/impl/connect/ConnectViewModel.kt
index 5972119456..eae192ccad 100644
--- a/android/lib/feature/home/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/home/impl/connect/ConnectViewModel.kt
+++ b/android/lib/feature/home/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/home/impl/connect/ConnectViewModel.kt
@@ -27,6 +27,7 @@ import net.mullvad.mullvadvpn.lib.model.ActionAfterDisconnect
import net.mullvad.mullvadvpn.lib.model.ConnectError
import net.mullvad.mullvadvpn.lib.model.DeviceState
import net.mullvad.mullvadvpn.lib.model.DisconnectReason
+import net.mullvad.mullvadvpn.lib.model.PackageName
import net.mullvad.mullvadvpn.lib.model.PrepareError
import net.mullvad.mullvadvpn.lib.model.TunnelState
import net.mullvad.mullvadvpn.lib.model.WebsiteAuthToken
@@ -61,7 +62,7 @@ class ConnectViewModel(
private val resources: Resources,
private val isPlayBuild: Boolean,
private val isFdroidBuild: Boolean,
- private val packageName: String,
+ private val self: PackageName,
) : ViewModel() {
private val _uiSideEffect = Channel<UiSideEffect>()
@@ -200,7 +201,7 @@ class ConnectViewModel(
val sideEffect =
if (isPlayBuild || isFdroidBuild) {
UiSideEffect.OpenUri(
- uri = resources.getString(R.string.market_uri, packageName).toUri(),
+ uri = resources.getString(R.string.market_uri, self.value).toUri(),
errorMessage = resources.getString(R.string.uri_market_app_not_found),
)
} else {
diff --git a/android/lib/feature/home/impl/src/test/kotlin/net/mullvad/mullvadvpn/feature/home/impl/connect/ConnectViewModelTest.kt b/android/lib/feature/home/impl/src/test/kotlin/net/mullvad/mullvadvpn/feature/home/impl/connect/ConnectViewModelTest.kt
index 732341cbbd..141d2e6695 100644
--- a/android/lib/feature/home/impl/src/test/kotlin/net/mullvad/mullvadvpn/feature/home/impl/connect/ConnectViewModelTest.kt
+++ b/android/lib/feature/home/impl/src/test/kotlin/net/mullvad/mullvadvpn/feature/home/impl/connect/ConnectViewModelTest.kt
@@ -26,6 +26,7 @@ import net.mullvad.mullvadvpn.lib.model.DisconnectReason
import net.mullvad.mullvadvpn.lib.model.ErrorState
import net.mullvad.mullvadvpn.lib.model.GeoIpLocation
import net.mullvad.mullvadvpn.lib.model.InAppNotification
+import net.mullvad.mullvadvpn.lib.model.PackageName
import net.mullvad.mullvadvpn.lib.model.TunnelEndpoint
import net.mullvad.mullvadvpn.lib.model.TunnelState
import net.mullvad.mullvadvpn.lib.model.WebsiteAuthToken
@@ -126,7 +127,7 @@ class ConnectViewModelTest {
resources = mockk(),
isPlayBuild = false,
isFdroidBuild = false,
- packageName = "net.mullvad.mullvadvpn",
+ self = PackageName("net.mullvad.mullvadvpn"),
)
}
diff --git a/android/lib/feature/language/impl/src/main/java/net/mullvad/mullvadvpn/feature/language/impl/LanguageScreen.kt b/android/lib/feature/language/impl/src/main/java/net/mullvad/mullvadvpn/feature/language/impl/LanguageScreen.kt
index 872a2500e6..f8334a986f 100644
--- a/android/lib/feature/language/impl/src/main/java/net/mullvad/mullvadvpn/feature/language/impl/LanguageScreen.kt
+++ b/android/lib/feature/language/impl/src/main/java/net/mullvad/mullvadvpn/feature/language/impl/LanguageScreen.kt
@@ -19,8 +19,8 @@ import net.mullvad.mullvadvpn.common.compose.unlessIsDetail
import net.mullvad.mullvadvpn.core.Navigator
import net.mullvad.mullvadvpn.lib.common.Lc
import net.mullvad.mullvadvpn.lib.common.toLc
-import net.mullvad.mullvadvpn.lib.ui.component.NavigateBackIconButton
import net.mullvad.mullvadvpn.lib.ui.component.ScaffoldWithMediumTopBar
+import net.mullvad.mullvadvpn.lib.ui.component.button.NavigateBackIconButton
import net.mullvad.mullvadvpn.lib.ui.component.listitem.SelectableListItem
import net.mullvad.mullvadvpn.lib.ui.designsystem.MullvadCircularProgressIndicatorLarge
import net.mullvad.mullvadvpn.lib.ui.designsystem.Position
diff --git a/android/lib/feature/location/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/location/impl/RelayListContent.kt b/android/lib/feature/location/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/location/impl/RelayListContent.kt
index ed13c57fbd..53538d45df 100644
--- a/android/lib/feature/location/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/location/impl/RelayListContent.kt
+++ b/android/lib/feature/location/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/location/impl/RelayListContent.kt
@@ -106,7 +106,7 @@ fun LazyListScope.relayListContent(
@Composable
private fun LocationsEmptyText(searchTerm: String) {
Text(
- text = stringResource(R.string.search_location_empty_text, searchTerm),
+ text = stringResource(R.string.search_no_matches_for_text, searchTerm),
style = MaterialTheme.typography.bodyMedium,
textAlign = TextAlign.Center,
color = MaterialTheme.colorScheme.onSurfaceVariant,
diff --git a/android/lib/feature/location/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/location/impl/SelectLocationScreen.kt b/android/lib/feature/location/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/location/impl/SelectLocationScreen.kt
index 27c562f87b..0ec1e6f18b 100644
--- a/android/lib/feature/location/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/location/impl/SelectLocationScreen.kt
+++ b/android/lib/feature/location/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/location/impl/SelectLocationScreen.kt
@@ -104,6 +104,7 @@ import net.mullvad.mullvadvpn.lib.model.RelayListType
import net.mullvad.mullvadvpn.lib.ui.component.MultihopSelector
import net.mullvad.mullvadvpn.lib.ui.component.ScaffoldWithSmallTopBar
import net.mullvad.mullvadvpn.lib.ui.component.Singlehop
+import net.mullvad.mullvadvpn.lib.ui.component.button.SearchButton
import net.mullvad.mullvadvpn.lib.ui.designsystem.MullvadCircularProgressIndicatorLarge
import net.mullvad.mullvadvpn.lib.ui.icon.DeleteHistory
import net.mullvad.mullvadvpn.lib.ui.tag.SELECT_LOCATION_MENU_BUTTON_TEST_TAG
@@ -111,7 +112,6 @@ import net.mullvad.mullvadvpn.lib.ui.tag.SELECT_LOCATION_SCREEN_TEST_TAG
import net.mullvad.mullvadvpn.lib.ui.theme.AppTheme
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.AlphaVisible
import net.mullvad.mullvadvpn.lib.usecase.FilterChip
import org.koin.androidx.compose.koinViewModel
@@ -417,19 +417,10 @@ fun SelectLocationScreen(
actions = {
if (isTv()) {
val isSearchButtonEnabled = state.contentOrNull()?.isSearchButtonEnabled == true
- IconButton(
+ SearchButton(
enabled = isSearchButtonEnabled,
onClick = { state.contentOrNull()?.let { onSearchClick(it.relayListType) } },
- ) {
- Icon(
- imageVector = Icons.Rounded.Search,
- contentDescription = stringResource(id = R.string.search),
- tint =
- MaterialTheme.colorScheme.onSurface.copy(
- alpha = if (isSearchButtonEnabled) AlphaVisible else AlphaDisabled
- ),
- )
- }
+ )
}
val filterButtonEnabled = state.contentOrNull()?.isFilterButtonEnabled == true
val recentsCurrentlyEnabled = state.contentOrNull()?.isRecentsEnabled == true
diff --git a/android/lib/feature/location/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/location/impl/search/SearchLocationScreen.kt b/android/lib/feature/location/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/location/impl/search/SearchLocationScreen.kt
index 7e60f86d9b..28b6fcc6ab 100644
--- a/android/lib/feature/location/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/location/impl/search/SearchLocationScreen.kt
+++ b/android/lib/feature/location/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/location/impl/search/SearchLocationScreen.kt
@@ -3,22 +3,13 @@ package net.mullvad.mullvadvpn.feature.location.impl.search
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.foundation.lazy.rememberLazyListState
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.automirrored.rounded.ArrowBack
-import androidx.compose.material.icons.rounded.Close
-import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.HorizontalDivider
-import androidx.compose.material3.Icon
-import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
-import androidx.compose.material3.SearchBarDefaults
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Text
@@ -61,8 +52,8 @@ import net.mullvad.mullvadvpn.lib.model.CustomListId
import net.mullvad.mullvadvpn.lib.model.RelayItem
import net.mullvad.mullvadvpn.lib.model.RelayItemId
import net.mullvad.mullvadvpn.lib.model.RelayListType
+import net.mullvad.mullvadvpn.lib.ui.component.MullvadSearchBar
import net.mullvad.mullvadvpn.lib.ui.component.drawVerticalScrollbar
-import net.mullvad.mullvadvpn.lib.ui.component.textfield.mullvadDarkTextFieldColors
import net.mullvad.mullvadvpn.lib.ui.designsystem.ListHeader
import net.mullvad.mullvadvpn.lib.ui.designsystem.MullvadCircularProgressIndicatorLarge
import net.mullvad.mullvadvpn.lib.ui.designsystem.MullvadSnackbar
@@ -197,7 +188,6 @@ fun SearchLocation(relayListType: RelayListType, navigator: Navigator) {
}
@Suppress("LongMethod", "LongParameterList")
-@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun SearchLocationScreen(
state: Lce<Unit, SearchLocationUiState, Unit>,
@@ -222,7 +212,7 @@ fun SearchLocationScreen(
Column(modifier = Modifier.padding(it)) {
val focusRequester = remember { FocusRequester() }
LaunchedEffect(state is Lce.Content) { focusRequester.requestFocus() }
- SearchBar(
+ MullvadSearchBar(
modifier = Modifier.focusRequester(focusRequester),
searchTerm = state.contentOrNull()?.searchTerm ?: "",
enabled = state is Lce.Content,
@@ -296,55 +286,6 @@ fun SearchLocationScreen(
}
}
-@OptIn(ExperimentalMaterial3Api::class)
-@Composable
-private fun SearchBar(
- searchTerm: String,
- enabled: Boolean,
- onSearchInputChanged: (String) -> Unit,
- hideKeyboard: () -> Unit,
- onGoBack: () -> Unit,
- modifier: Modifier = Modifier,
-) {
- SearchBarDefaults.InputField(
- modifier = modifier.height(Dimens.searchFieldHeightExpanded).fillMaxWidth(),
- query = searchTerm,
- enabled = enabled,
- onQueryChange = onSearchInputChanged,
- onSearch = { hideKeyboard() },
- expanded = true,
- onExpandedChange = {},
- leadingIcon = {
- IconButton(onClick = onGoBack) {
- Icon(
- imageVector = Icons.AutoMirrored.Rounded.ArrowBack,
- contentDescription = stringResource(R.string.back),
- )
- }
- },
- trailingIcon = {
- if (searchTerm.isNotEmpty()) {
- IconButton(onClick = { onSearchInputChanged("") }) {
- Icon(
- imageVector = Icons.Rounded.Close,
- contentDescription = stringResource(R.string.clear_input),
- )
- }
- }
- },
- placeholder = { Text(text = stringResource(id = R.string.search_placeholder)) },
- colors =
- mullvadDarkTextFieldColors()
- .copy(
- focusedContainerColor = MaterialTheme.colorScheme.surface,
- unfocusedContainerColor = MaterialTheme.colorScheme.surface,
- errorContainerColor = MaterialTheme.colorScheme.surface,
- disabledContainerColor = MaterialTheme.colorScheme.surface,
- disabledLeadingIconColor = MaterialTheme.colorScheme.onSurface,
- ),
- )
-}
-
private fun LazyListScope.filterRow(
filters: List<FilterChip>,
onRemoveOwnershipFilter: () -> Unit,
diff --git a/android/lib/feature/managedevices/impl/src/main/java/net/mullvad/mullvadvpn/feature/managedevices/impl/ManageDevicesScreen.kt b/android/lib/feature/managedevices/impl/src/main/java/net/mullvad/mullvadvpn/feature/managedevices/impl/ManageDevicesScreen.kt
index a4d25c6925..20e04cec2a 100644
--- a/android/lib/feature/managedevices/impl/src/main/java/net/mullvad/mullvadvpn/feature/managedevices/impl/ManageDevicesScreen.kt
+++ b/android/lib/feature/managedevices/impl/src/main/java/net/mullvad/mullvadvpn/feature/managedevices/impl/ManageDevicesScreen.kt
@@ -34,8 +34,8 @@ import net.mullvad.mullvadvpn.lib.common.Lce
import net.mullvad.mullvadvpn.lib.model.AccountNumber
import net.mullvad.mullvadvpn.lib.model.Device
import net.mullvad.mullvadvpn.lib.model.GetDeviceListError
-import net.mullvad.mullvadvpn.lib.ui.component.NavigateBackIconButton
import net.mullvad.mullvadvpn.lib.ui.component.ScaffoldWithMediumTopBar
+import net.mullvad.mullvadvpn.lib.ui.component.button.NavigateBackIconButton
import net.mullvad.mullvadvpn.lib.ui.component.listitem.DeviceListItem
import net.mullvad.mullvadvpn.lib.ui.component.positionForIndex
import net.mullvad.mullvadvpn.lib.ui.designsystem.MullvadCircularProgressIndicatorMedium
diff --git a/android/lib/feature/multihop/impl/src/main/java/net/mullvad/mullvadvpn/feature/multihop/impl/MultihopScreen.kt b/android/lib/feature/multihop/impl/src/main/java/net/mullvad/mullvadvpn/feature/multihop/impl/MultihopScreen.kt
index 478eb23c11..2058dd324d 100644
--- a/android/lib/feature/multihop/impl/src/main/java/net/mullvad/mullvadvpn/feature/multihop/impl/MultihopScreen.kt
+++ b/android/lib/feature/multihop/impl/src/main/java/net/mullvad/mullvadvpn/feature/multihop/impl/MultihopScreen.kt
@@ -27,9 +27,9 @@ import net.mullvad.mullvadvpn.common.compose.unlessIsDetail
import net.mullvad.mullvadvpn.core.Navigator
import net.mullvad.mullvadvpn.lib.common.Lc
import net.mullvad.mullvadvpn.lib.model.FeatureIndicator
-import net.mullvad.mullvadvpn.lib.ui.component.NavigateBackIconButton
-import net.mullvad.mullvadvpn.lib.ui.component.NavigateCloseIconButton
import net.mullvad.mullvadvpn.lib.ui.component.ScaffoldWithMediumTopBar
+import net.mullvad.mullvadvpn.lib.ui.component.button.NavigateBackIconButton
+import net.mullvad.mullvadvpn.lib.ui.component.button.NavigateCloseIconButton
import net.mullvad.mullvadvpn.lib.ui.component.listitem.SwitchListItem
import net.mullvad.mullvadvpn.lib.ui.component.text.ScreenDescription
import net.mullvad.mullvadvpn.lib.ui.designsystem.MullvadCircularProgressIndicatorLarge
diff --git a/android/lib/feature/notification/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/notification/impl/NotificationSettingsScreen.kt b/android/lib/feature/notification/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/notification/impl/NotificationSettingsScreen.kt
index 999512b802..7758bc6c1d 100644
--- a/android/lib/feature/notification/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/notification/impl/NotificationSettingsScreen.kt
+++ b/android/lib/feature/notification/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/notification/impl/NotificationSettingsScreen.kt
@@ -27,8 +27,8 @@ import net.mullvad.mullvadvpn.common.compose.unlessIsDetail
import net.mullvad.mullvadvpn.core.Navigator
import net.mullvad.mullvadvpn.lib.common.Lc
import net.mullvad.mullvadvpn.lib.common.util.openAppInfoNotificationSettings
-import net.mullvad.mullvadvpn.lib.ui.component.NavigateBackIconButton
import net.mullvad.mullvadvpn.lib.ui.component.ScaffoldWithMediumTopBar
+import net.mullvad.mullvadvpn.lib.ui.component.button.NavigateBackIconButton
import net.mullvad.mullvadvpn.lib.ui.component.listitem.SwitchListItem
import net.mullvad.mullvadvpn.lib.ui.designsystem.MullvadCircularProgressIndicatorLarge
import net.mullvad.mullvadvpn.lib.ui.designsystem.PrimaryButton
diff --git a/android/lib/feature/problemreport/impl/src/main/java/net/mullvad/mullvadvpn/feature/problemreport/impl/ReportProblemScreen.kt b/android/lib/feature/problemreport/impl/src/main/java/net/mullvad/mullvadvpn/feature/problemreport/impl/ReportProblemScreen.kt
index 898b134e1f..3bdb92d3ce 100644
--- a/android/lib/feature/problemreport/impl/src/main/java/net/mullvad/mullvadvpn/feature/problemreport/impl/ReportProblemScreen.kt
+++ b/android/lib/feature/problemreport/impl/src/main/java/net/mullvad/mullvadvpn/feature/problemreport/impl/ReportProblemScreen.kt
@@ -57,8 +57,8 @@ import net.mullvad.mullvadvpn.feature.problemreport.api.ViewLogsNavKey
import net.mullvad.mullvadvpn.lib.common.util.appendHideNavOnPlayBuild
import net.mullvad.mullvadvpn.lib.ui.component.CheckboxConfirmation
import net.mullvad.mullvadvpn.lib.ui.component.ExpandChevron
-import net.mullvad.mullvadvpn.lib.ui.component.NavigateBackIconButton
import net.mullvad.mullvadvpn.lib.ui.component.ScaffoldWithMediumTopBar
+import net.mullvad.mullvadvpn.lib.ui.component.button.NavigateBackIconButton
import net.mullvad.mullvadvpn.lib.ui.component.textfield.mullvadDarkTextFieldColors
import net.mullvad.mullvadvpn.lib.ui.designsystem.MullvadCircularProgressIndicatorLarge
import net.mullvad.mullvadvpn.lib.ui.designsystem.PrimaryButton
diff --git a/android/lib/feature/problemreport/impl/src/main/java/net/mullvad/mullvadvpn/feature/problemreport/impl/viewlogs/ViewLogsScreen.kt b/android/lib/feature/problemreport/impl/src/main/java/net/mullvad/mullvadvpn/feature/problemreport/impl/viewlogs/ViewLogsScreen.kt
index 2b46145f76..59b196f225 100644
--- a/android/lib/feature/problemreport/impl/src/main/java/net/mullvad/mullvadvpn/feature/problemreport/impl/viewlogs/ViewLogsScreen.kt
+++ b/android/lib/feature/problemreport/impl/src/main/java/net/mullvad/mullvadvpn/feature/problemreport/impl/viewlogs/ViewLogsScreen.kt
@@ -47,7 +47,7 @@ import net.mullvad.mullvadvpn.core.Navigator
import net.mullvad.mullvadvpn.feature.problemreport.impl.provider.getLogsShareIntent
import net.mullvad.mullvadvpn.lib.common.Lc
import net.mullvad.mullvadvpn.lib.ui.component.MullvadMediumTopBar
-import net.mullvad.mullvadvpn.lib.ui.component.NavigateBackIconButton
+import net.mullvad.mullvadvpn.lib.ui.component.button.NavigateBackIconButton
import net.mullvad.mullvadvpn.lib.ui.component.drawVerticalScrollbar
import net.mullvad.mullvadvpn.lib.ui.designsystem.MullvadCircularProgressIndicatorMedium
import net.mullvad.mullvadvpn.lib.ui.designsystem.MullvadSnackbar
diff --git a/android/lib/feature/serveripoverride/impl/src/main/java/net/mullvad/mullvadvpn/feature/serveripoverride/impl/ServerIpOverridesScreen.kt b/android/lib/feature/serveripoverride/impl/src/main/java/net/mullvad/mullvadvpn/feature/serveripoverride/impl/ServerIpOverridesScreen.kt
index f79027809d..e81ae089e3 100644
--- a/android/lib/feature/serveripoverride/impl/src/main/java/net/mullvad/mullvadvpn/feature/serveripoverride/impl/ServerIpOverridesScreen.kt
+++ b/android/lib/feature/serveripoverride/impl/src/main/java/net/mullvad/mullvadvpn/feature/serveripoverride/impl/ServerIpOverridesScreen.kt
@@ -56,9 +56,9 @@ import net.mullvad.mullvadvpn.feature.serveripoverride.api.ServerIpOverrideNavKe
import net.mullvad.mullvadvpn.lib.common.Lc
import net.mullvad.mullvadvpn.lib.model.FeatureIndicator
import net.mullvad.mullvadvpn.lib.model.SettingsPatchError
-import net.mullvad.mullvadvpn.lib.ui.component.NavigateBackIconButton
-import net.mullvad.mullvadvpn.lib.ui.component.NavigateCloseIconButton
import net.mullvad.mullvadvpn.lib.ui.component.ScaffoldWithMediumTopBar
+import net.mullvad.mullvadvpn.lib.ui.component.button.NavigateBackIconButton
+import net.mullvad.mullvadvpn.lib.ui.component.button.NavigateCloseIconButton
import net.mullvad.mullvadvpn.lib.ui.component.listitem.ServerIpOverridesListItem
import net.mullvad.mullvadvpn.lib.ui.designsystem.MullvadSnackbar
import net.mullvad.mullvadvpn.lib.ui.designsystem.PrimaryButton
diff --git a/android/lib/feature/settings/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/settings/impl/SettingsScreen.kt b/android/lib/feature/settings/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/settings/impl/SettingsScreen.kt
index dca81319f8..992562d841 100644
--- a/android/lib/feature/settings/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/settings/impl/SettingsScreen.kt
+++ b/android/lib/feature/settings/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/settings/impl/SettingsScreen.kt
@@ -41,8 +41,8 @@ import net.mullvad.mullvadvpn.feature.splittunneling.api.SplitTunnelingNavKey
import net.mullvad.mullvadvpn.feature.vpnsettings.api.VpnSettingsNavKey
import net.mullvad.mullvadvpn.lib.common.Lc
import net.mullvad.mullvadvpn.lib.common.util.appendHideNavOnPlayBuild
-import net.mullvad.mullvadvpn.lib.ui.component.NavigateCloseIconButton
import net.mullvad.mullvadvpn.lib.ui.component.ScaffoldWithMediumTopBar
+import net.mullvad.mullvadvpn.lib.ui.component.button.NavigateCloseIconButton
import net.mullvad.mullvadvpn.lib.ui.component.listitem.ExternalLinkListItem
import net.mullvad.mullvadvpn.lib.ui.component.listitem.NavigationListItem
import net.mullvad.mullvadvpn.lib.ui.designsystem.MullvadCircularProgressIndicatorLarge
diff --git a/android/lib/feature/splittunneling/api/src/main/kotlin/net/mullvad/mullvadvpn/feature/splittunneling/api/SearchSplitTunnelingNavKey.kt b/android/lib/feature/splittunneling/api/src/main/kotlin/net/mullvad/mullvadvpn/feature/splittunneling/api/SearchSplitTunnelingNavKey.kt
new file mode 100644
index 0000000000..b491525cc4
--- /dev/null
+++ b/android/lib/feature/splittunneling/api/src/main/kotlin/net/mullvad/mullvadvpn/feature/splittunneling/api/SearchSplitTunnelingNavKey.kt
@@ -0,0 +1,6 @@
+package net.mullvad.mullvadvpn.feature.splittunneling.api
+
+import kotlinx.parcelize.Parcelize
+import net.mullvad.mullvadvpn.core.NavKey2
+
+@Parcelize data object SearchSplitTunnelingNavKey : NavKey2
diff --git a/android/lib/feature/splittunneling/impl/src/androidTest/kotlin/net/mullvad/mullvadvpn/feature/splittunneling/impl/SplitTunnelingScreenTest.kt b/android/lib/feature/splittunneling/impl/src/androidTest/kotlin/net/mullvad/mullvadvpn/feature/splittunneling/impl/SplitTunnelingScreenTest.kt
index f33686f074..776305c756 100644
--- a/android/lib/feature/splittunneling/impl/src/androidTest/kotlin/net/mullvad/mullvadvpn/feature/splittunneling/impl/SplitTunnelingScreenTest.kt
+++ b/android/lib/feature/splittunneling/impl/src/androidTest/kotlin/net/mullvad/mullvadvpn/feature/splittunneling/impl/SplitTunnelingScreenTest.kt
@@ -12,6 +12,7 @@ import io.mockk.verify
import net.mullvad.mullvadvpn.feature.splittunneling.impl.applist.AppData
import net.mullvad.mullvadvpn.lib.common.Lc
import net.mullvad.mullvadvpn.lib.common.toLc
+import net.mullvad.mullvadvpn.lib.model.PackageName
import net.mullvad.mullvadvpn.screen.test.createEdgeToEdgeComposeExtension
import net.mullvad.mullvadvpn.screen.test.setContentWithTheme
import org.junit.jupiter.api.AfterEach
@@ -37,10 +38,11 @@ class SplitTunnelingScreenTest {
state: Lc<Loading, SplitTunnelingUiState>,
onEnableSplitTunneling: (Boolean) -> Unit = {},
onShowSystemAppsClick: (show: Boolean) -> Unit = {},
- onExcludeAppClick: (packageName: String) -> Unit = {},
- onIncludeAppClick: (packageName: String) -> Unit = {},
+ onExcludeAppClick: (packageName: PackageName) -> Unit = {},
+ onIncludeAppClick: (packageName: PackageName) -> Unit = {},
onBackClick: () -> Unit = {},
- onResolveIcon: (String) -> Drawable? = { null },
+ onResolveIcon: (PackageName) -> Drawable? = { null },
+ navigateToSearch: () -> Unit = {},
) {
setContentWithTheme {
SplitTunnelingScreen(
@@ -51,6 +53,7 @@ class SplitTunnelingScreenTest {
onIncludeAppClick = onIncludeAppClick,
onBackClick = onBackClick,
onResolveIcon = onResolveIcon,
+ navigateToSearch = navigateToSearch,
)
}
}
@@ -58,7 +61,7 @@ class SplitTunnelingScreenTest {
@Test
fun testLoadingState() = composeExtension.use {
// Arrange
- initScreen(state = Lc.Loading(Loading(enabled = true)))
+ initScreen(state = Lc.Loading(Loading()))
// Assert
onNodeWithText(TITLE).assertExists()
@@ -129,7 +132,7 @@ class SplitTunnelingScreenTest {
AppData(packageName = EXCLUDED_APP_PACKAGE_NAME, iconRes = 0, name = EXCLUDED_APP_NAME)
val includedApp =
AppData(packageName = INCLUDED_APP_PACKAGE_NAME, iconRes = 0, name = INCLUDED_APP_NAME)
- val mockedClickHandler: (String) -> Unit = mockk(relaxed = true)
+ val mockedClickHandler: (PackageName) -> Unit = mockk(relaxed = true)
initScreen(
state =
SplitTunnelingUiState(
@@ -156,7 +159,7 @@ class SplitTunnelingScreenTest {
AppData(packageName = EXCLUDED_APP_PACKAGE_NAME, iconRes = 0, name = EXCLUDED_APP_NAME)
val includedApp =
AppData(packageName = INCLUDED_APP_PACKAGE_NAME, iconRes = 0, name = INCLUDED_APP_NAME)
- val mockedClickHandler: (String) -> Unit = mockk(relaxed = true)
+ val mockedClickHandler: (PackageName) -> Unit = mockk(relaxed = true)
initScreen(
state =
SplitTunnelingUiState(
@@ -204,9 +207,9 @@ class SplitTunnelingScreenTest {
}
companion object {
- private const val EXCLUDED_APP_PACKAGE_NAME = "excluded-pkg"
+ private val EXCLUDED_APP_PACKAGE_NAME = PackageName("excluded-pkg")
private const val EXCLUDED_APP_NAME = "Excluded Name"
- private const val INCLUDED_APP_PACKAGE_NAME = "included-pkg"
+ private val INCLUDED_APP_PACKAGE_NAME = PackageName("included-pkg")
private const val INCLUDED_APP_NAME = "Included Name"
private const val TITLE = "Split tunneling"
private const val DESCRIPTION =
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 f647531047..62b87e60f6 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
@@ -40,14 +40,17 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import net.mullvad.mullvadvpn.common.compose.unlessIsDetail
import net.mullvad.mullvadvpn.core.Navigator
+import net.mullvad.mullvadvpn.feature.splittunneling.api.SearchSplitTunnelingNavKey
import net.mullvad.mullvadvpn.feature.splittunneling.impl.applist.AppData
import net.mullvad.mullvadvpn.feature.splittunneling.impl.extensions.hasValidSize
import net.mullvad.mullvadvpn.feature.splittunneling.impl.extensions.isBelowMaxByteSize
import net.mullvad.mullvadvpn.lib.common.Lc
import net.mullvad.mullvadvpn.lib.model.FeatureIndicator
-import net.mullvad.mullvadvpn.lib.ui.component.NavigateBackIconButton
-import net.mullvad.mullvadvpn.lib.ui.component.NavigateCloseIconButton
+import net.mullvad.mullvadvpn.lib.model.PackageName
import net.mullvad.mullvadvpn.lib.ui.component.ScaffoldWithMediumTopBar
+import net.mullvad.mullvadvpn.lib.ui.component.button.NavigateBackIconButton
+import net.mullvad.mullvadvpn.lib.ui.component.button.NavigateCloseIconButton
+import net.mullvad.mullvadvpn.lib.ui.component.button.SearchButton
import net.mullvad.mullvadvpn.lib.ui.component.listitem.IconState
import net.mullvad.mullvadvpn.lib.ui.component.listitem.SplitTunnelingListItem
import net.mullvad.mullvadvpn.lib.ui.component.listitem.SwitchListItem
@@ -76,6 +79,7 @@ private fun PreviewSplitTunnelingScreen(
onExcludeAppClick = {},
onIncludeAppClick = {},
onBackClick = {},
+ navigateToSearch = {},
onResolveIcon = { null },
)
}
@@ -104,6 +108,7 @@ fun SharedTransitionScope.SplitTunneling(
onExcludeAppClick = viewModel::onExcludeAppClick,
onIncludeAppClick = viewModel::onIncludeAppClick,
onBackClick = dropUnlessResumed { navigator.goBack() },
+ navigateToSearch = dropUnlessResumed { navigator.navigate(SearchSplitTunnelingNavKey) },
onResolveIcon = { packageName -> packageManager.getApplicationIconOrNull(packageName) },
)
}
@@ -113,10 +118,11 @@ fun SplitTunnelingScreen(
state: Lc<Loading, SplitTunnelingUiState>,
onEnableSplitTunneling: (Boolean) -> Unit,
onShowSystemAppsClick: (show: Boolean) -> Unit,
- onExcludeAppClick: (packageName: String) -> Unit,
- onIncludeAppClick: (packageName: String) -> Unit,
+ onExcludeAppClick: (packageName: PackageName) -> Unit,
+ onIncludeAppClick: (packageName: PackageName) -> Unit,
onBackClick: () -> Unit,
- onResolveIcon: (String) -> Drawable?,
+ onResolveIcon: (PackageName) -> Drawable?,
+ navigateToSearch: () -> Unit,
modifier: Modifier = Modifier,
) {
val focusManager = LocalFocusManager.current
@@ -131,6 +137,7 @@ fun SplitTunnelingScreen(
unlessIsDetail { NavigateBackIconButton(onNavigateBack = onBackClick) }
}
},
+ actions = { SearchButton(onClick = navigateToSearch, enabled = state.enabled()) },
) { modifier, lazyListState: LazyListState ->
LazyColumn(
modifier =
@@ -141,20 +148,25 @@ fun SplitTunnelingScreen(
state = lazyListState,
) {
description()
- enabledToggle(
- enabled = state.enabled(),
- onEnableSplitTunneling = onEnableSplitTunneling,
- )
when (state) {
is Lc.Loading -> {
spacer()
loading()
}
is Lc.Content -> {
+ enabledToggle(
+ enabled = state.value.enabled,
+ onEnableSplitTunneling = onEnableSplitTunneling,
+ )
+ item { HorizontalDivider(color = Color.Transparent) }
+ systemAppsToggle(
+ showSystemApps = state.value.showSystemApps,
+ onShowSystemAppsClick = onShowSystemAppsClick,
+ enabled = state.value.enabled,
+ )
appList(
state = state.value,
focusManager = focusManager,
- onShowSystemAppsClick = onShowSystemAppsClick,
onExcludeAppClick = onExcludeAppClick,
onIncludeAppClick = onIncludeAppClick,
onResolveIcon = onResolveIcon,
@@ -174,6 +186,7 @@ private fun LazyListScope.enabledToggle(
title = stringResource(id = R.string.enable),
isToggled = enabled,
onCellClicked = onEnableSplitTunneling,
+ position = Position.Top,
)
}
}
@@ -200,10 +213,9 @@ private fun LazyListScope.loading() {
private fun LazyListScope.appList(
state: SplitTunnelingUiState,
focusManager: FocusManager,
- onShowSystemAppsClick: (show: Boolean) -> Unit,
- onExcludeAppClick: (packageName: String) -> Unit,
- onIncludeAppClick: (packageName: String) -> Unit,
- onResolveIcon: (String) -> Drawable?,
+ onExcludeAppClick: (packageName: PackageName) -> Unit,
+ onIncludeAppClick: (packageName: PackageName) -> Unit,
+ onResolveIcon: (PackageName) -> Drawable?,
) {
if (state.excludedApps.isNotEmpty()) {
headerItem(
@@ -221,11 +233,6 @@ private fun LazyListScope.appList(
)
}
spacer()
- systemAppsToggle(
- showSystemApps = state.showSystemApps,
- onShowSystemAppsClick = onShowSystemAppsClick,
- enabled = state.enabled,
- )
headerItem(
key = SplitTunnelingContentKey.INCLUDED_APPLICATIONS,
textId = R.string.all_applications,
@@ -242,17 +249,17 @@ private fun LazyListScope.appList(
spacer()
}
-private fun LazyListScope.appItems(
+internal fun LazyListScope.appItems(
apps: List<AppData>,
focusManager: FocusManager,
- onAppClick: (String) -> Unit,
- onResolveIcon: (String) -> Drawable?,
+ onAppClick: (PackageName) -> Unit,
+ onResolveIcon: (PackageName) -> Drawable?,
enabled: Boolean,
excluded: Boolean,
) {
itemsIndexedWithDivider(
items = apps,
- key = { _, listItem -> listItem.packageName },
+ key = { _, listItem -> listItem.packageName.value },
contentType = { _, _ -> ContentType.ITEM },
) { index, listItem ->
val packageName = listItem.packageName
@@ -303,7 +310,7 @@ private fun LazyListScope.appItems(
}
}
-private fun LazyListScope.headerItem(key: String, textId: Int, enabled: Boolean) {
+internal fun LazyListScope.headerItem(key: String, textId: Int, enabled: Boolean) {
itemWithDivider(key = key, contentType = ContentType.HEADER) {
ListHeader(
modifier =
@@ -320,7 +327,7 @@ private fun LazyListScope.headerItem(key: String, textId: Int, enabled: Boolean)
}
}
-private fun LazyListScope.systemAppsToggle(
+internal fun LazyListScope.systemAppsToggle(
showSystemApps: Boolean,
onShowSystemAppsClick: (show: Boolean) -> Unit,
enabled: Boolean,
@@ -341,7 +348,7 @@ private fun LazyListScope.systemAppsToggle(
} else {
AlphaDisabled
},
- position = Position.Single,
+ position = Position.Bottom,
)
}
}
@@ -354,19 +361,19 @@ private fun LazyListScope.spacer() {
private fun Lc<Loading, SplitTunnelingUiState>.isModal(): Boolean =
when (this) {
- is Lc.Loading -> this.value.isModal
- is Lc.Content -> this.value.isModal
+ is Lc.Loading -> value.isModal
+ is Lc.Content -> value.isModal
}
private fun Lc<Loading, SplitTunnelingUiState>.enabled(): Boolean =
when (this) {
- is Lc.Loading -> this.value.enabled
- is Lc.Content -> this.value.enabled
+ is Lc.Loading -> false
+ is Lc.Content -> value.enabled
}
-fun PackageManager.getApplicationIconOrNull(packageName: String): Drawable? =
+fun PackageManager.getApplicationIconOrNull(packageName: PackageName): Drawable? =
try {
- getApplicationIcon(packageName)
+ getApplicationIcon(packageName.value)
} catch (e: PackageManager.NameNotFoundException) {
// Name not found is thrown if the application is not installed
null
diff --git a/android/lib/feature/splittunneling/impl/src/main/java/net/mullvad/mullvadvpn/feature/splittunneling/impl/SplitTunnelingUiState.kt b/android/lib/feature/splittunneling/impl/src/main/java/net/mullvad/mullvadvpn/feature/splittunneling/impl/SplitTunnelingUiState.kt
index 7bb091ea1c..7913fab030 100644
--- a/android/lib/feature/splittunneling/impl/src/main/java/net/mullvad/mullvadvpn/feature/splittunneling/impl/SplitTunnelingUiState.kt
+++ b/android/lib/feature/splittunneling/impl/src/main/java/net/mullvad/mullvadvpn/feature/splittunneling/impl/SplitTunnelingUiState.kt
@@ -2,7 +2,7 @@ package net.mullvad.mullvadvpn.feature.splittunneling.impl
import net.mullvad.mullvadvpn.feature.splittunneling.impl.applist.AppData
-data class Loading(val enabled: Boolean = false, val isModal: Boolean = false)
+data class Loading(val isModal: Boolean = false)
data class SplitTunnelingUiState(
val enabled: Boolean = false,
diff --git a/android/lib/feature/splittunneling/impl/src/main/java/net/mullvad/mullvadvpn/feature/splittunneling/impl/SplitTunnelingUiStatePreviewParameterProvider.kt b/android/lib/feature/splittunneling/impl/src/main/java/net/mullvad/mullvadvpn/feature/splittunneling/impl/SplitTunnelingUiStatePreviewParameterProvider.kt
index 94524e1909..88781a12b8 100644
--- a/android/lib/feature/splittunneling/impl/src/main/java/net/mullvad/mullvadvpn/feature/splittunneling/impl/SplitTunnelingUiStatePreviewParameterProvider.kt
+++ b/android/lib/feature/splittunneling/impl/src/main/java/net/mullvad/mullvadvpn/feature/splittunneling/impl/SplitTunnelingUiStatePreviewParameterProvider.kt
@@ -4,6 +4,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import net.mullvad.mullvadvpn.feature.splittunneling.impl.applist.AppData
import net.mullvad.mullvadvpn.lib.common.Lc
import net.mullvad.mullvadvpn.lib.common.toLc
+import net.mullvad.mullvadvpn.lib.model.PackageName
import net.mullvad.mullvadvpn.lib.ui.resource.R
class SplitTunnelingUiStatePreviewParameterProvider :
@@ -24,16 +25,24 @@ class SplitTunnelingUiStatePreviewParameterProvider :
showSystemApps = false,
)
.toLc(),
- Lc.Loading(Loading(enabled = true)),
+ Lc.Loading(Loading()),
)
}
private val excludedApps =
listOf(
- AppData(packageName = "my.package.a", name = "TitleA", iconRes = R.drawable.icon_android),
- AppData(packageName = "my.package.b", name = "TitleB", iconRes = R.drawable.icon_android),
AppData(
- packageName = "my.package.c",
+ packageName = PackageName("my.package.a"),
+ name = "TitleA",
+ iconRes = R.drawable.icon_android,
+ ),
+ AppData(
+ packageName = PackageName("my.package.b"),
+ name = "TitleB",
+ iconRes = R.drawable.icon_android,
+ ),
+ AppData(
+ packageName = PackageName("my.package.c"),
name = "TitleC (System app)",
iconRes = R.drawable.icon_android,
isSystemApp = true,
@@ -41,9 +50,13 @@ private val excludedApps =
)
private val includedApps =
listOf(
- AppData(packageName = "my.package.d", name = "TitleD", iconRes = R.drawable.icon_android),
AppData(
- packageName = "my.package.e",
+ packageName = PackageName("my.package.d"),
+ name = "TitleD",
+ iconRes = R.drawable.icon_android,
+ ),
+ AppData(
+ packageName = PackageName("my.package.e"),
name = "TitleE (System app)",
iconRes = R.drawable.icon_android,
isSystemApp = true,
diff --git a/android/lib/feature/splittunneling/impl/src/main/java/net/mullvad/mullvadvpn/feature/splittunneling/impl/SplitTunnelingViewModel.kt b/android/lib/feature/splittunneling/impl/src/main/java/net/mullvad/mullvadvpn/feature/splittunneling/impl/SplitTunnelingViewModel.kt
index 96c0971d58..4db3b65138 100644
--- a/android/lib/feature/splittunneling/impl/src/main/java/net/mullvad/mullvadvpn/feature/splittunneling/impl/SplitTunnelingViewModel.kt
+++ b/android/lib/feature/splittunneling/impl/src/main/java/net/mullvad/mullvadvpn/feature/splittunneling/impl/SplitTunnelingViewModel.kt
@@ -3,95 +3,66 @@ package net.mullvad.mullvadvpn.feature.splittunneling.impl
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.CoroutineDispatcher
-import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.WhileSubscribed
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
-import net.mullvad.mullvadvpn.feature.splittunneling.impl.applist.AppData
-import net.mullvad.mullvadvpn.feature.splittunneling.impl.applist.ApplicationsProvider
+import net.mullvad.mullvadvpn.feature.splittunneling.impl.applist.SplitTunnelingUseCase
import net.mullvad.mullvadvpn.lib.common.Lc
import net.mullvad.mullvadvpn.lib.common.constant.VIEW_MODEL_STOP_TIMEOUT
-import net.mullvad.mullvadvpn.lib.common.toLc
-import net.mullvad.mullvadvpn.lib.model.AppId
+import net.mullvad.mullvadvpn.lib.model.PackageName
import net.mullvad.mullvadvpn.lib.repository.SplitTunnelingRepository
+import net.mullvad.mullvadvpn.lib.repository.UserPreferencesRepository
class SplitTunnelingViewModel(
isModal: Boolean,
- private val appsProvider: ApplicationsProvider,
private val splitTunnelingRepository: SplitTunnelingRepository,
+ private val userPreferencesRepository: UserPreferencesRepository,
+ splitTunnelingUseCase: SplitTunnelingUseCase,
private val dispatcher: CoroutineDispatcher,
) : ViewModel() {
- private val allApps = MutableStateFlow<List<AppData>?>(null)
- private val showSystemApps = MutableStateFlow(false)
-
val uiState: StateFlow<Lc<Loading, SplitTunnelingUiState>> =
combine(
- splitTunnelingRepository.excludedApps,
+ splitTunnelingUseCase(),
splitTunnelingRepository.splitTunnelingEnabled,
- allApps,
- showSystemApps,
- ) { excludedApps, enabled, allApps, showSystemApps ->
- if (allApps == null) {
- return@combine Lc.Loading(Loading(enabled = enabled, isModal = isModal))
- }
-
- val (excludedApps, includedApps) =
- allApps.partition { appData ->
- if (enabled) {
- excludedApps.contains(AppId(appData.packageName))
- } else {
- false
- }
- }
-
- SplitTunnelingUiState(
+ userPreferencesRepository.showSystemAppsSplitTunneling(),
+ ) { splitApps, enabled, showSystemApps ->
+ Lc.Content(
+ SplitTunnelingUiState(
enabled = enabled,
- excludedApps = excludedApps,
- includedApps =
- if (showSystemApps) includedApps
- else includedApps.filter { appData -> !appData.isSystemApp },
+ excludedApps = splitApps.excludedApps,
+ includedApps = splitApps.includedApps,
showSystemApps = showSystemApps,
isModal = isModal,
)
- .toLc()
+ )
}
.stateIn(
viewModelScope,
SharingStarted.WhileSubscribed(VIEW_MODEL_STOP_TIMEOUT),
- Lc.Loading(Loading(enabled = false, isModal = isModal)),
+ Lc.Loading(Loading(isModal = isModal)),
)
- init {
- viewModelScope.launch(dispatcher) { fetchApps() }
- }
-
fun onEnableSplitTunneling(isEnabled: Boolean) {
viewModelScope.launch(dispatcher) {
splitTunnelingRepository.enableSplitTunneling(isEnabled)
}
}
- fun onIncludeAppClick(packageName: String) {
- viewModelScope.launch(dispatcher) {
- splitTunnelingRepository.includeApp(AppId(packageName))
- }
+ fun onIncludeAppClick(packageName: PackageName) {
+ viewModelScope.launch(dispatcher) { splitTunnelingRepository.includeApp(packageName) }
}
- fun onExcludeAppClick(packageName: String) {
- viewModelScope.launch(dispatcher) {
- splitTunnelingRepository.excludeApp(AppId(packageName))
- }
+ fun onExcludeAppClick(packageName: PackageName) {
+ viewModelScope.launch(dispatcher) { splitTunnelingRepository.excludeApp(packageName) }
}
fun onShowSystemAppsClick(show: Boolean) {
- viewModelScope.launch(dispatcher) { showSystemApps.emit(show) }
- }
-
- private suspend fun fetchApps() {
- appsProvider.apps().let { appsList -> allApps.emit(appsList) }
+ viewModelScope.launch(dispatcher) {
+ userPreferencesRepository.setShowSystemAppsSplitTunneling(show)
+ }
}
}
diff --git a/android/lib/feature/splittunneling/impl/src/main/java/net/mullvad/mullvadvpn/feature/splittunneling/impl/applist/AppData.kt b/android/lib/feature/splittunneling/impl/src/main/java/net/mullvad/mullvadvpn/feature/splittunneling/impl/applist/AppData.kt
index 9c199567bf..6fc59bab58 100644
--- a/android/lib/feature/splittunneling/impl/src/main/java/net/mullvad/mullvadvpn/feature/splittunneling/impl/applist/AppData.kt
+++ b/android/lib/feature/splittunneling/impl/src/main/java/net/mullvad/mullvadvpn/feature/splittunneling/impl/applist/AppData.kt
@@ -1,7 +1,9 @@
package net.mullvad.mullvadvpn.feature.splittunneling.impl.applist
+import net.mullvad.mullvadvpn.lib.model.PackageName
+
data class AppData(
- val packageName: String,
+ val packageName: PackageName,
val iconRes: Int,
val name: String,
val isSystemApp: Boolean = false,
diff --git a/android/lib/feature/splittunneling/impl/src/main/java/net/mullvad/mullvadvpn/feature/splittunneling/impl/applist/ApplicationsProvider.kt b/android/lib/feature/splittunneling/impl/src/main/java/net/mullvad/mullvadvpn/feature/splittunneling/impl/applist/ApplicationsProvider.kt
index c913a865c2..0421eab563 100644
--- a/android/lib/feature/splittunneling/impl/src/main/java/net/mullvad/mullvadvpn/feature/splittunneling/impl/applist/ApplicationsProvider.kt
+++ b/android/lib/feature/splittunneling/impl/src/main/java/net/mullvad/mullvadvpn/feature/splittunneling/impl/applist/ApplicationsProvider.kt
@@ -3,10 +3,11 @@ package net.mullvad.mullvadvpn.feature.splittunneling.impl.applist
import android.Manifest
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
+import net.mullvad.mullvadvpn.lib.model.PackageName
class ApplicationsProvider(
private val packageManager: PackageManager,
- private val thisPackageName: String,
+ private val self: PackageName,
) {
private val applicationFilterPredicate: (ApplicationInfo) -> Boolean = { appInfo ->
hasInternetPermission(appInfo.packageName) && !isSelfApplication(appInfo.packageName)
@@ -21,7 +22,7 @@ class ApplicationsProvider(
.filter(applicationFilterPredicate)
.map { info ->
AppData(
- info.packageName,
+ PackageName(info.packageName),
info.icon,
info.loadLabel(packageManager).toString(),
!isLaunchable(info.packageName),
@@ -42,6 +43,6 @@ class ApplicationsProvider(
}
private fun isSelfApplication(packageName: String): Boolean {
- return packageName == thisPackageName
+ return packageName == self.value
}
}
diff --git a/android/lib/feature/splittunneling/impl/src/main/java/net/mullvad/mullvadvpn/feature/splittunneling/impl/applist/SplitTunnelingUseCase.kt b/android/lib/feature/splittunneling/impl/src/main/java/net/mullvad/mullvadvpn/feature/splittunneling/impl/applist/SplitTunnelingUseCase.kt
new file mode 100644
index 0000000000..ce4a2e2de1
--- /dev/null
+++ b/android/lib/feature/splittunneling/impl/src/main/java/net/mullvad/mullvadvpn/feature/splittunneling/impl/applist/SplitTunnelingUseCase.kt
@@ -0,0 +1,44 @@
+package net.mullvad.mullvadvpn.feature.splittunneling.impl.applist
+
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.flowOf
+import net.mullvad.mullvadvpn.lib.model.PackageName
+import net.mullvad.mullvadvpn.lib.repository.SplitTunnelingRepository
+import net.mullvad.mullvadvpn.lib.repository.UserPreferencesRepository
+
+class SplitTunnelingUseCase(
+ private val splitTunnelingRepository: SplitTunnelingRepository,
+ private val applicationsProvider: ApplicationsProvider,
+ private val preferencesRepository: UserPreferencesRepository,
+) {
+ operator fun invoke(): Flow<SplitApps> =
+ combine(
+ flowOf(applicationsProvider.apps()),
+ splitTunnelingRepository.excludedApps,
+ splitTunnelingRepository.splitTunnelingEnabled,
+ preferencesRepository.showSystemAppsSplitTunneling(),
+ ) { allApps, exclusions, splitTunnelingEnabled, showSystemApps ->
+ val exclusions = if (splitTunnelingEnabled) exclusions else emptySet()
+ SplitApps(
+ allApps =
+ if (showSystemApps) allApps
+ else allApps.filter { !it.isSystemApp || it.packageName in exclusions },
+ exclusions = exclusions,
+ )
+ }
+}
+
+data class SplitApps(private val allApps: List<AppData>, private val exclusions: Set<PackageName>) {
+ val includedApps: List<AppData>
+ val excludedApps: List<AppData>
+
+ init {
+ allApps
+ .partition { appData -> exclusions.contains(appData.packageName) }
+ .also { (exclusions, inclusions) ->
+ includedApps = inclusions
+ excludedApps = exclusions
+ }
+ }
+}
diff --git a/android/lib/feature/splittunneling/impl/src/main/java/net/mullvad/mullvadvpn/feature/splittunneling/impl/navigation/SearchSplitTunnelingEntryProvider.kt b/android/lib/feature/splittunneling/impl/src/main/java/net/mullvad/mullvadvpn/feature/splittunneling/impl/navigation/SearchSplitTunnelingEntryProvider.kt
new file mode 100644
index 0000000000..0d2016bc65
--- /dev/null
+++ b/android/lib/feature/splittunneling/impl/src/main/java/net/mullvad/mullvadvpn/feature/splittunneling/impl/navigation/SearchSplitTunnelingEntryProvider.kt
@@ -0,0 +1,17 @@
+package net.mullvad.mullvadvpn.feature.splittunneling.impl.navigation
+
+import androidx.navigation3.runtime.EntryProviderScope
+import net.mullvad.mullvadvpn.core.NavKey2
+import net.mullvad.mullvadvpn.core.Navigator
+import net.mullvad.mullvadvpn.core.animation.slideInHorizontalTransition
+import net.mullvad.mullvadvpn.core.scene.ListDetailSceneStrategy
+import net.mullvad.mullvadvpn.feature.splittunneling.api.SearchSplitTunnelingNavKey
+import net.mullvad.mullvadvpn.feature.splittunneling.impl.search.SearchSplitTunnelingScreen
+
+fun EntryProviderScope<NavKey2>.searchSplitTunnelingEntry(navigator: Navigator) {
+ entry<SearchSplitTunnelingNavKey>(
+ metadata = ListDetailSceneStrategy.detailPane() + slideInHorizontalTransition()
+ ) { _ ->
+ SearchSplitTunnelingScreen(navigator = navigator)
+ }
+}
diff --git a/android/lib/feature/splittunneling/impl/src/main/java/net/mullvad/mullvadvpn/feature/splittunneling/impl/navigation/SplitTunnelingEntryProvider.kt b/android/lib/feature/splittunneling/impl/src/main/java/net/mullvad/mullvadvpn/feature/splittunneling/impl/navigation/SplitTunnelingEntryProvider.kt
index f6594bc21e..ed5128ba70 100644
--- a/android/lib/feature/splittunneling/impl/src/main/java/net/mullvad/mullvadvpn/feature/splittunneling/impl/navigation/SplitTunnelingEntryProvider.kt
+++ b/android/lib/feature/splittunneling/impl/src/main/java/net/mullvad/mullvadvpn/feature/splittunneling/impl/navigation/SplitTunnelingEntryProvider.kt
@@ -20,4 +20,5 @@ fun EntryProviderScope<NavKey2>.splitTunnelingEntry(navigator: Navigator) {
animatedVisibilityScope = LocalNavAnimatedContentScope.current,
)
}
+ searchSplitTunnelingEntry(navigator)
}
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
new file mode 100644
index 0000000000..13c7cef23d
--- /dev/null
+++ b/android/lib/feature/splittunneling/impl/src/main/java/net/mullvad/mullvadvpn/feature/splittunneling/impl/search/SearchSplitTunnelingScreen.kt
@@ -0,0 +1,203 @@
+package net.mullvad.mullvadvpn.feature.splittunneling.impl.search
+
+import android.graphics.drawable.Drawable
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.consumeWindowInsets
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.imePadding
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.LazyListScope
+import androidx.compose.foundation.lazy.rememberLazyListState
+import androidx.compose.material3.HorizontalDivider
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Scaffold
+import androidx.compose.material3.SnackbarHost
+import androidx.compose.material3.SnackbarHostState
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.focus.FocusManager
+import androidx.compose.ui.focus.FocusRequester
+import androidx.compose.ui.focus.focusRequester
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.platform.LocalFocusManager
+import androidx.compose.ui.platform.LocalSoftwareKeyboardController
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.text.style.TextOverflow
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import androidx.lifecycle.compose.dropUnlessResumed
+import net.mullvad.mullvadvpn.core.Navigator
+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.getApplicationIconOrNull
+import net.mullvad.mullvadvpn.feature.splittunneling.impl.headerItem
+import net.mullvad.mullvadvpn.lib.common.Lc
+import net.mullvad.mullvadvpn.lib.model.PackageName
+import net.mullvad.mullvadvpn.lib.ui.component.MullvadSearchBar
+import net.mullvad.mullvadvpn.lib.ui.component.drawVerticalScrollbar
+import net.mullvad.mullvadvpn.lib.ui.designsystem.MullvadCircularProgressIndicatorLarge
+import net.mullvad.mullvadvpn.lib.ui.designsystem.MullvadSnackbar
+import net.mullvad.mullvadvpn.lib.ui.resource.R
+import net.mullvad.mullvadvpn.lib.ui.theme.Dimens
+import net.mullvad.mullvadvpn.lib.ui.theme.color.AlphaScrollbar
+import org.koin.androidx.compose.koinViewModel
+
+@Composable
+fun SearchSplitTunnelingScreen(navigator: Navigator) {
+ val viewModel = koinViewModel<SearchSplitTunnelingViewModel>()
+ val state by viewModel.uiState.collectAsStateWithLifecycle()
+
+ SearchSplitTunnelingScreen(
+ state = state,
+ onSearchInputChanged = viewModel::onSearchInputChanged,
+ onExcludeAppClick = viewModel::onExcludeAppClick,
+ onIncludeAppClick = viewModel::onIncludeAppClick,
+ onGoBack = dropUnlessResumed { navigator.goBack() },
+ )
+}
+
+@Composable
+fun SearchSplitTunnelingScreen(
+ state: Lc<Unit, SearchSplitTunnelingUiState>,
+ snackbarHostState: SnackbarHostState = remember { SnackbarHostState() },
+ onSearchInputChanged: (String) -> Unit,
+ onExcludeAppClick: (packageName: PackageName) -> Unit,
+ onIncludeAppClick: (packageName: PackageName) -> Unit,
+ onGoBack: () -> Unit,
+) {
+ val keyboardController = LocalSoftwareKeyboardController.current
+ Scaffold(
+ snackbarHost = {
+ SnackbarHost(
+ snackbarHostState,
+ snackbar = { snackbarData -> MullvadSnackbar(snackbarData = snackbarData) },
+ )
+ }
+ ) {
+ val focusManager = LocalFocusManager.current
+ Column(modifier = Modifier.fillMaxSize().padding(it).consumeWindowInsets(it).imePadding()) {
+ val focusRequester = remember { FocusRequester() }
+ LaunchedEffect(state is Lc.Content) { focusRequester.requestFocus() }
+ MullvadSearchBar(
+ modifier = Modifier.focusRequester(focusRequester),
+ searchTerm = state.contentOrNull()?.searchTerm ?: "",
+ enabled = state is Lc.Content,
+ onSearchInputChanged = onSearchInputChanged,
+ hideKeyboard = { keyboardController?.hide() },
+ onGoBack = onGoBack,
+ )
+ HorizontalDivider(color = MaterialTheme.colorScheme.onSurface)
+ val lazyListState = rememberLazyListState()
+ val context = LocalContext.current
+ val packageManager = remember(context) { context.packageManager }
+ LazyColumn(
+ modifier =
+ Modifier.fillMaxSize()
+ .padding(horizontal = Dimens.mediumPadding)
+ .background(color = MaterialTheme.colorScheme.surface)
+ .drawVerticalScrollbar(
+ lazyListState,
+ MaterialTheme.colorScheme.onSurface.copy(alpha = AlphaScrollbar),
+ ),
+ state = lazyListState,
+ ) {
+ when (state) {
+ is Lc.Loading -> {
+ spacer()
+ loading()
+ }
+ is Lc.Content -> {
+ appList(
+ state = state.value,
+ focusManager = focusManager,
+ onExcludeAppClick = onExcludeAppClick,
+ onIncludeAppClick = onIncludeAppClick,
+ onResolveIcon = { packageName ->
+ packageManager.getApplicationIconOrNull(packageName)
+ },
+ )
+ }
+ }
+ }
+ }
+ }
+}
+
+private fun LazyListScope.loading() {
+ item(key = CommonContentKey.PROGRESS, contentType = ContentType.PROGRESS) {
+ MullvadCircularProgressIndicatorLarge()
+ }
+}
+
+private fun LazyListScope.spacer() {
+ item(contentType = ContentType.SPACER) {
+ Spacer(modifier = Modifier.animateItem().height(Dimens.cellVerticalSpacing))
+ }
+}
+
+private fun LazyListScope.appList(
+ state: SearchSplitTunnelingUiState,
+ focusManager: FocusManager,
+ onExcludeAppClick: (packageName: PackageName) -> Unit,
+ onIncludeAppClick: (packageName: PackageName) -> Unit,
+ onResolveIcon: (PackageName) -> Drawable?,
+) {
+ if (state.includedApps.isEmpty() && state.excludedApps.isEmpty()) {
+ item { NoAppsMatchingSearch(state.searchTerm) }
+ }
+ if (state.excludedApps.isNotEmpty()) {
+ headerItem(
+ key = SplitTunnelingContentKey.EXCLUDED_APPLICATIONS,
+ textId = R.string.exclude_applications,
+ enabled = true,
+ )
+ appItems(
+ apps = state.excludedApps,
+ focusManager = focusManager,
+ onAppClick = onIncludeAppClick,
+ onResolveIcon = onResolveIcon,
+ enabled = true,
+ excluded = true,
+ )
+ spacer()
+ }
+ if (state.includedApps.isNotEmpty()) {
+ headerItem(
+ key = SplitTunnelingContentKey.INCLUDED_APPLICATIONS,
+ textId = R.string.all_applications,
+ enabled = true,
+ )
+ appItems(
+ apps = state.includedApps,
+ focusManager = focusManager,
+ onAppClick = onExcludeAppClick,
+ onResolveIcon = onResolveIcon,
+ enabled = true,
+ excluded = false,
+ )
+ spacer()
+ }
+}
+
+@Composable
+private fun NoAppsMatchingSearch(searchTerm: String) {
+ Text(
+ text = stringResource(R.string.search_no_matches_for_text, searchTerm),
+ style = MaterialTheme.typography.bodyMedium,
+ textAlign = TextAlign.Center,
+ color = MaterialTheme.colorScheme.onSurfaceVariant,
+ maxLines = 2,
+ overflow = TextOverflow.Ellipsis,
+ modifier = Modifier.padding(Dimens.cellVerticalSpacing),
+ )
+}
diff --git a/android/lib/feature/splittunneling/impl/src/main/java/net/mullvad/mullvadvpn/feature/splittunneling/impl/search/SearchSplitTunnelingUiState.kt b/android/lib/feature/splittunneling/impl/src/main/java/net/mullvad/mullvadvpn/feature/splittunneling/impl/search/SearchSplitTunnelingUiState.kt
new file mode 100644
index 0000000000..ba119f777b
--- /dev/null
+++ b/android/lib/feature/splittunneling/impl/src/main/java/net/mullvad/mullvadvpn/feature/splittunneling/impl/search/SearchSplitTunnelingUiState.kt
@@ -0,0 +1,9 @@
+package net.mullvad.mullvadvpn.feature.splittunneling.impl.search
+
+import net.mullvad.mullvadvpn.feature.splittunneling.impl.applist.AppData
+
+data class SearchSplitTunnelingUiState(
+ val searchTerm: String,
+ val excludedApps: List<AppData> = emptyList(),
+ val includedApps: List<AppData> = emptyList(),
+)
diff --git a/android/lib/feature/splittunneling/impl/src/main/java/net/mullvad/mullvadvpn/feature/splittunneling/impl/search/SearchSplitTunnelingViewModel.kt b/android/lib/feature/splittunneling/impl/src/main/java/net/mullvad/mullvadvpn/feature/splittunneling/impl/search/SearchSplitTunnelingViewModel.kt
new file mode 100644
index 0000000000..112724fdb9
--- /dev/null
+++ b/android/lib/feature/splittunneling/impl/src/main/java/net/mullvad/mullvadvpn/feature/splittunneling/impl/search/SearchSplitTunnelingViewModel.kt
@@ -0,0 +1,63 @@
+package net.mullvad.mullvadvpn.feature.splittunneling.impl.search
+
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.WhileSubscribed
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.launch
+import net.mullvad.mullvadvpn.feature.splittunneling.impl.applist.SplitTunnelingUseCase
+import net.mullvad.mullvadvpn.lib.common.Lc
+import net.mullvad.mullvadvpn.lib.common.constant.VIEW_MODEL_STOP_TIMEOUT
+import net.mullvad.mullvadvpn.lib.model.PackageName
+import net.mullvad.mullvadvpn.lib.repository.SplitTunnelingRepository
+
+class SearchSplitTunnelingViewModel(
+ splitTunnelingUseCase: SplitTunnelingUseCase,
+ private val splitTunnelingRepository: SplitTunnelingRepository,
+ private val dispatcher: CoroutineDispatcher,
+) : ViewModel() {
+ private val _searchTerm = MutableStateFlow(EMPTY_SEARCH_TERM)
+
+ val uiState: StateFlow<Lc<Unit, SearchSplitTunnelingUiState>> =
+ combine(splitTunnelingUseCase(), _searchTerm) { splitApps, searchTerm ->
+ Lc.Content(
+ SearchSplitTunnelingUiState(
+ searchTerm = searchTerm,
+ excludedApps =
+ splitApps.excludedApps.filter {
+ it.name.contains(searchTerm, ignoreCase = true)
+ },
+ includedApps =
+ splitApps.includedApps.filter {
+ it.name.contains(searchTerm, ignoreCase = true)
+ },
+ )
+ )
+ }
+ .stateIn(
+ viewModelScope,
+ SharingStarted.WhileSubscribed(VIEW_MODEL_STOP_TIMEOUT),
+ Lc.Loading(Unit),
+ )
+
+ fun onSearchInputChanged(searchTerm: String) {
+ viewModelScope.launch { _searchTerm.emit(searchTerm) }
+ }
+
+ fun onIncludeAppClick(packageName: PackageName) {
+ viewModelScope.launch(dispatcher) { splitTunnelingRepository.includeApp(packageName) }
+ }
+
+ fun onExcludeAppClick(packageName: PackageName) {
+ viewModelScope.launch(dispatcher) { splitTunnelingRepository.excludeApp(packageName) }
+ }
+
+ companion object {
+ private const val EMPTY_SEARCH_TERM = ""
+ }
+}
diff --git a/android/lib/feature/splittunneling/impl/src/test/java/net/mullvad/mullvadvpn/feature/splittunneling/impl/SplitTunnelingViewModelTest.kt b/android/lib/feature/splittunneling/impl/src/test/java/net/mullvad/mullvadvpn/feature/splittunneling/impl/SplitTunnelingViewModelTest.kt
index 09ddf5170e..8df0b133bf 100644
--- a/android/lib/feature/splittunneling/impl/src/test/java/net/mullvad/mullvadvpn/feature/splittunneling/impl/SplitTunnelingViewModelTest.kt
+++ b/android/lib/feature/splittunneling/impl/src/test/java/net/mullvad/mullvadvpn/feature/splittunneling/impl/SplitTunnelingViewModelTest.kt
@@ -19,10 +19,12 @@ import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
import net.mullvad.mullvadvpn.feature.splittunneling.impl.applist.AppData
import net.mullvad.mullvadvpn.feature.splittunneling.impl.applist.ApplicationsProvider
+import net.mullvad.mullvadvpn.feature.splittunneling.impl.applist.SplitTunnelingUseCase
import net.mullvad.mullvadvpn.lib.common.Lc
import net.mullvad.mullvadvpn.lib.common.test.TestCoroutineRule
-import net.mullvad.mullvadvpn.lib.model.AppId
+import net.mullvad.mullvadvpn.lib.model.PackageName
import net.mullvad.mullvadvpn.lib.repository.SplitTunnelingRepository
+import net.mullvad.mullvadvpn.lib.repository.UserPreferencesRepository
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
@@ -36,15 +38,19 @@ class SplitTunnelingViewModelTest {
private val mockedApplicationsProvider = mockk<ApplicationsProvider>()
private val mockedSplitTunnelingRepository = mockk<SplitTunnelingRepository>()
+ private val mockedUserPreferencesRepository = mockk<UserPreferencesRepository>()
private lateinit var testSubject: SplitTunnelingViewModel
- private val excludedApps: MutableStateFlow<Set<AppId>> = MutableStateFlow(emptySet())
+ private val excludedApps: MutableStateFlow<Set<PackageName>> = MutableStateFlow(emptySet())
private val enabled: MutableStateFlow<Boolean> = MutableStateFlow(true)
+ private val showSystemApps: MutableStateFlow<Boolean> = MutableStateFlow(false)
@BeforeEach
fun setup() {
every { mockedSplitTunnelingRepository.splitTunnelingEnabled } returns enabled
every { mockedSplitTunnelingRepository.excludedApps } returns excludedApps
+ every { mockedUserPreferencesRepository.showSystemAppsSplitTunneling() } returns
+ showSystemApps
}
@AfterEach
@@ -58,7 +64,7 @@ class SplitTunnelingViewModelTest {
initTestSubject(emptyList())
val actualState: Lc<Loading, SplitTunnelingUiState> = testSubject.uiState.value
- val initialExpectedState = Lc.Loading(Loading(enabled = false))
+ val initialExpectedState = Lc.Loading(Loading())
assertIs<Lc.Loading<Loading>>(actualState)
assertEquals(initialExpectedState, actualState)
@@ -85,11 +91,11 @@ class SplitTunnelingViewModelTest {
@Test
fun `includedApps and excludedApps should both be included in uiState`() = runTest {
- val appExcluded = AppData("test.excluded", 0, "testName1")
- val appNotExcluded = AppData("test.not.excluded", 0, "testName2")
+ val appExcluded = AppData(PackageName("test.excluded"), 0, "testName1")
+ val appNotExcluded = AppData(PackageName("test.not.excluded"), 0, "testName2")
initTestSubject(listOf(appExcluded, appNotExcluded))
- excludedApps.value = setOf(AppId(appExcluded.packageName))
+ excludedApps.value = setOf(appExcluded.packageName)
val expectedState =
SplitTunnelingUiState(
@@ -108,10 +114,10 @@ class SplitTunnelingViewModelTest {
@Test
fun `include app should work`() = runTest {
- val app = AppData("test", 0, "testName")
+ val app = AppData(PackageName("test"), 0, "testName")
initTestSubject(listOf(app))
- excludedApps.value = setOf(AppId(app.packageName))
+ excludedApps.value = setOf(app.packageName)
val expectedStateBeforeAction =
SplitTunnelingUiState(
@@ -127,8 +133,7 @@ class SplitTunnelingViewModelTest {
includedApps = listOf(app),
showSystemApps = false,
)
- coEvery { mockedSplitTunnelingRepository.includeApp(AppId(app.packageName)) } returns
- Unit.right()
+ coEvery { mockedSplitTunnelingRepository.includeApp(app.packageName) } returns Unit.right()
testSubject.uiState.test {
val beforeAction = awaitItem()
@@ -140,13 +145,13 @@ class SplitTunnelingViewModelTest {
assertIs<Lc.Content<SplitTunnelingUiState>>(afterAction)
assertEquals(expectedStateAfterAction, afterAction.value)
- coVerify { mockedSplitTunnelingRepository.includeApp(AppId(app.packageName)) }
+ coVerify { mockedSplitTunnelingRepository.includeApp(app.packageName) }
}
}
@Test
fun `onExcludeApp should result in new uiState with app excluded`() = runTest {
- val app = AppData("test", 0, "testName")
+ val app = AppData(PackageName("test"), 0, "testName")
initTestSubject(listOf(app))
@@ -166,20 +171,19 @@ class SplitTunnelingViewModelTest {
showSystemApps = false,
)
- coEvery { mockedSplitTunnelingRepository.excludeApp(AppId(app.packageName)) } returns
- Unit.right()
+ coEvery { mockedSplitTunnelingRepository.excludeApp(app.packageName) } returns Unit.right()
testSubject.uiState.test {
val beforeAction = awaitItem()
assertIs<Lc.Content<SplitTunnelingUiState>>(beforeAction)
assertEquals(expectedStateBeforeAction, beforeAction.value)
testSubject.onExcludeAppClick(app.packageName)
- excludedApps.value = setOf(AppId(app.packageName))
+ excludedApps.value = setOf(app.packageName)
val afterAction = awaitItem()
assertIs<Lc.Content<SplitTunnelingUiState>>(afterAction)
assertEquals(expectedStateAfterAction, afterAction.value)
- coVerify { mockedSplitTunnelingRepository.excludeApp(AppId(app.packageName)) }
+ coVerify { mockedSplitTunnelingRepository.excludeApp(app.packageName) }
}
}
@@ -202,8 +206,13 @@ class SplitTunnelingViewModelTest {
testSubject =
SplitTunnelingViewModel(
isModal = false,
- mockedApplicationsProvider,
mockedSplitTunnelingRepository,
+ mockedUserPreferencesRepository,
+ SplitTunnelingUseCase(
+ mockedSplitTunnelingRepository,
+ mockedApplicationsProvider,
+ mockedUserPreferencesRepository,
+ ),
UnconfinedTestDispatcher(),
)
}
diff --git a/android/lib/feature/splittunneling/impl/src/test/java/net/mullvad/mullvadvpn/feature/splittunneling/impl/applist/ApplicationsProviderTest.kt b/android/lib/feature/splittunneling/impl/src/test/java/net/mullvad/mullvadvpn/feature/splittunneling/impl/applist/ApplicationsProviderTest.kt
index 2ffbb8f34a..ebe071994c 100644
--- a/android/lib/feature/splittunneling/impl/src/test/java/net/mullvad/mullvadvpn/feature/splittunneling/impl/applist/ApplicationsProviderTest.kt
+++ b/android/lib/feature/splittunneling/impl/src/test/java/net/mullvad/mullvadvpn/feature/splittunneling/impl/applist/ApplicationsProviderTest.kt
@@ -10,13 +10,15 @@ import io.mockk.unmockkAll
import io.mockk.verifyAll
import kotlin.test.assertEquals
import net.mullvad.mullvadvpn.lib.common.test.assertLists
+import net.mullvad.mullvadvpn.lib.model.PackageName
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.Test
class ApplicationsProviderTest {
private val mockedPackageManager = mockk<PackageManager>()
- private val selfPackageName = "self_package_name"
- private val testSubject = ApplicationsProvider(mockedPackageManager, selfPackageName)
+ private val self = PackageName("self_package_name")
+ private val testSubject = ApplicationsProvider(mockedPackageManager, self)
+
private val internet = Manifest.permission.INTERNET
@AfterEach
@@ -27,50 +29,33 @@ class ApplicationsProviderTest {
@SuppressLint("UseCheckPermission")
@Test
fun `fetch all apps should work`() {
- val launchWithInternetPackageName = "launch_with_internet_package_name"
- val launchWithoutInternetPackageName = "launch_without_internet_package_name"
- val nonLaunchWithInternetPackageName = "non_launch_with_internet_package_name"
- val nonLaunchWithoutInternetPackageName = "non_launch_without_internet_package_name"
- val leanbackLaunchWithInternetPackageName = "leanback_launch_with_internet_package_name"
- val leanbackLaunchWithoutInternetPackageName =
- "leanback_launch_without_internet_package_name"
+ val launchWithInternet = PackageName("launch_with_internet_package_name")
+ val launchWithoutInternet = PackageName("launch_without_internet_package_name")
+ val nonLaunchWithInternet = PackageName("non_launch_with_internet_package_name")
+ val nonLaunchWithoutInternet = PackageName("non_launch_without_internet_package_name")
+ val leanbackLaunchWithInternet = PackageName("leanback_launch_with_internet_package_name")
+ val leanbackLaunchWithoutInternet =
+ PackageName("leanback_launch_without_internet_package_name")
every {
mockedPackageManager.getInstalledApplications(PackageManager.GET_META_DATA)
} returns
listOf(
- createApplicationInfo(
- launchWithInternetPackageName,
- launch = true,
- internet = true,
- ),
- createApplicationInfo(launchWithoutInternetPackageName, launch = true),
- createApplicationInfo(nonLaunchWithInternetPackageName, internet = true),
- createApplicationInfo(nonLaunchWithoutInternetPackageName),
- createApplicationInfo(
- leanbackLaunchWithInternetPackageName,
- leanback = true,
- internet = true,
- ),
- createApplicationInfo(leanbackLaunchWithoutInternetPackageName, leanback = true),
- createApplicationInfo(selfPackageName, internet = true, launch = true),
+ createApplicationInfo(launchWithInternet, launch = true, internet = true),
+ createApplicationInfo(launchWithoutInternet, launch = true),
+ createApplicationInfo(nonLaunchWithInternet, internet = true),
+ createApplicationInfo(nonLaunchWithoutInternet),
+ createApplicationInfo(leanbackLaunchWithInternet, leanback = true, internet = true),
+ createApplicationInfo(leanbackLaunchWithoutInternet, leanback = true),
+ createApplicationInfo(self, internet = true, launch = true),
)
val result = testSubject.apps()
val expected =
listOf(
- AppData(launchWithInternetPackageName, 0, launchWithInternetPackageName),
- AppData(
- nonLaunchWithInternetPackageName,
- 0,
- nonLaunchWithInternetPackageName,
- true,
- ),
- AppData(
- leanbackLaunchWithInternetPackageName,
- 0,
- leanbackLaunchWithInternetPackageName,
- ),
+ AppData(launchWithInternet, 0, launchWithInternet.value),
+ AppData(nonLaunchWithInternet, 0, nonLaunchWithInternet.value, true),
+ AppData(leanbackLaunchWithInternet, 0, leanbackLaunchWithInternet.value),
)
assertLists(expected, result)
@@ -80,51 +65,46 @@ class ApplicationsProviderTest {
// Ensure checkPermission was invoked on all packages
listOf(
- launchWithInternetPackageName,
- launchWithoutInternetPackageName,
- nonLaunchWithInternetPackageName,
- nonLaunchWithoutInternetPackageName,
- leanbackLaunchWithInternetPackageName,
- leanbackLaunchWithoutInternetPackageName,
- selfPackageName,
+ launchWithInternet,
+ launchWithoutInternet,
+ nonLaunchWithInternet,
+ nonLaunchWithoutInternet,
+ leanbackLaunchWithInternet,
+ leanbackLaunchWithoutInternet,
+ self,
)
.forEach { packageName ->
- mockedPackageManager.checkPermission(internet, packageName)
+ mockedPackageManager.checkPermission(internet, packageName.value)
}
- listOf(
- launchWithInternetPackageName,
- nonLaunchWithInternetPackageName,
- leanbackLaunchWithInternetPackageName,
- )
- .forEach { packageName ->
- mockedPackageManager.getLaunchIntentForPackage(packageName)
- }
+ listOf(launchWithInternet, nonLaunchWithInternet, leanbackLaunchWithInternet).forEach {
+ packageName ->
+ mockedPackageManager.getLaunchIntentForPackage(packageName.value)
+ }
- listOf(nonLaunchWithInternetPackageName, leanbackLaunchWithInternetPackageName)
- .forEach { packageName ->
- mockedPackageManager.getLeanbackLaunchIntentForPackage(packageName)
- }
+ listOf(nonLaunchWithInternet, leanbackLaunchWithInternet).forEach { packageName ->
+ mockedPackageManager.getLeanbackLaunchIntentForPackage(packageName.value)
+ }
}
}
@SuppressLint("UseCheckPermission")
@Test
fun `apps should be returned in descending order`() {
- val packageNames = listOf("b", "d", "c", "a", "e")
+ val packageNames = listOf("b", "d", "c", "a", "e").map { PackageName(it) }
every {
mockedPackageManager.getInstalledApplications(PackageManager.GET_META_DATA)
} returns packageNames.map { createApplicationInfo(it, launch = true, internet = true) }
val actual = testSubject.apps()
- val expected = packageNames.sorted().map { AppData(it, 0, it) }
+ val expected = packageNames.sortedBy { it.value }.map { AppData(it, 0, it.value) }
assertEquals(expected, actual)
}
private fun createApplicationInfo(
- packageName: String,
+ packageName: PackageName,
launch: Boolean = false,
leanback: Boolean = false,
internet: Boolean = false,
@@ -132,19 +112,19 @@ class ApplicationsProviderTest {
): ApplicationInfo {
val mockApplicationInfo = mockk<ApplicationInfo>()
- mockApplicationInfo.packageName = packageName
+ mockApplicationInfo.packageName = packageName.value
mockApplicationInfo.icon = 0
- every { mockApplicationInfo.loadLabel(mockedPackageManager) } returns packageName
+ every { mockApplicationInfo.loadLabel(mockedPackageManager) } returns packageName.value
- every { mockedPackageManager.getLaunchIntentForPackage(packageName) } returns
+ every { mockedPackageManager.getLaunchIntentForPackage(packageName.value) } returns
if (launch || systemApp) mockk() else null
- every { mockedPackageManager.getLeanbackLaunchIntentForPackage(packageName) } returns
+ every { mockedPackageManager.getLeanbackLaunchIntentForPackage(packageName.value) } returns
if (leanback || systemApp) mockk() else null
every {
- mockedPackageManager.checkPermission(Manifest.permission.INTERNET, packageName)
+ mockedPackageManager.checkPermission(Manifest.permission.INTERNET, packageName.value)
} returns
if (internet) PackageManager.PERMISSION_GRANTED else PackageManager.PERMISSION_DENIED
diff --git a/android/lib/feature/vpnsettings/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/vpnsettings/impl/VpnSettingsScreen.kt b/android/lib/feature/vpnsettings/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/vpnsettings/impl/VpnSettingsScreen.kt
index dec9b9e077..5a22c15410 100644
--- a/android/lib/feature/vpnsettings/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/vpnsettings/impl/VpnSettingsScreen.kt
+++ b/android/lib/feature/vpnsettings/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/vpnsettings/impl/VpnSettingsScreen.kt
@@ -81,9 +81,9 @@ import net.mullvad.mullvadvpn.lib.model.IpVersion
import net.mullvad.mullvadvpn.lib.model.Mtu
import net.mullvad.mullvadvpn.lib.ui.component.DividerButton
import net.mullvad.mullvadvpn.lib.ui.component.MullvadMediumTopBar
-import net.mullvad.mullvadvpn.lib.ui.component.NavigateBackIconButton
-import net.mullvad.mullvadvpn.lib.ui.component.NavigateCloseIconButton
import net.mullvad.mullvadvpn.lib.ui.component.SPACE_CHAR
+import net.mullvad.mullvadvpn.lib.ui.component.button.NavigateBackIconButton
+import net.mullvad.mullvadvpn.lib.ui.component.button.NavigateCloseIconButton
import net.mullvad.mullvadvpn.lib.ui.component.drawVerticalScrollbar
import net.mullvad.mullvadvpn.lib.ui.component.listitem.DnsListItem
import net.mullvad.mullvadvpn.lib.ui.component.listitem.ExpandableListItem
diff --git a/android/lib/grpc/src/main/kotlin/net/mullvad/mullvadvpn/lib/grpc/ManagementService.kt b/android/lib/grpc/src/main/kotlin/net/mullvad/mullvadvpn/lib/grpc/ManagementService.kt
index 05266556f7..11d106db76 100644
--- a/android/lib/grpc/src/main/kotlin/net/mullvad/mullvadvpn/lib/grpc/ManagementService.kt
+++ b/android/lib/grpc/src/main/kotlin/net/mullvad/mullvadvpn/lib/grpc/ManagementService.kt
@@ -54,7 +54,6 @@ import net.mullvad.mullvadvpn.lib.model.AddSplitTunnelingAppError
import net.mullvad.mullvadvpn.lib.model.ApiAccessMethod
import net.mullvad.mullvadvpn.lib.model.ApiAccessMethodId
import net.mullvad.mullvadvpn.lib.model.ApiAccessMethodSetting
-import net.mullvad.mullvadvpn.lib.model.AppId
import net.mullvad.mullvadvpn.lib.model.AppVersionInfo as ModelAppVersionInfo
import net.mullvad.mullvadvpn.lib.model.ClearAccountHistoryError
import net.mullvad.mullvadvpn.lib.model.ClearAllOverridesError
@@ -93,6 +92,7 @@ import net.mullvad.mullvadvpn.lib.model.NewAccessMethodSetting
import net.mullvad.mullvadvpn.lib.model.ObfuscationMode
import net.mullvad.mullvadvpn.lib.model.ObfuscationSettings
import net.mullvad.mullvadvpn.lib.model.Ownership as ModelOwnership
+import net.mullvad.mullvadvpn.lib.model.PackageName
import net.mullvad.mullvadvpn.lib.model.PlayExternalObfuscatedAccountId
import net.mullvad.mullvadvpn.lib.model.PlayPurchase
import net.mullvad.mullvadvpn.lib.model.PlayPurchaseInitError
@@ -808,13 +808,15 @@ class ManagementService(
.mapLeft { PlayPurchaseVerifyError.OtherError }
.mapEmpty()
- suspend fun addSplitTunnelingApp(app: AppId): Either<AddSplitTunnelingAppError, Unit> =
+ suspend fun addSplitTunnelingApp(app: PackageName): Either<AddSplitTunnelingAppError, Unit> =
Either.catch { grpc.addSplitTunnelApp(StringValue.of(app.value)) }
.onLeft { Logger.e("Add split tunneling app error") }
.mapLeft(AddSplitTunnelingAppError::Unknown)
.mapEmpty()
- suspend fun removeSplitTunnelingApp(app: AppId): Either<RemoveSplitTunnelingAppError, Unit> =
+ suspend fun removeSplitTunnelingApp(
+ app: PackageName
+ ): Either<RemoveSplitTunnelingAppError, Unit> =
Either.catch { grpc.removeSplitTunnelApp(StringValue.of(app.value)) }
.onLeft { Logger.e("Remove split tunneling app error") }
.mapLeft(RemoveSplitTunnelingAppError::Unknown)
diff --git a/android/lib/grpc/src/main/kotlin/net/mullvad/mullvadvpn/lib/grpc/mapper/ToDomain.kt b/android/lib/grpc/src/main/kotlin/net/mullvad/mullvadvpn/lib/grpc/mapper/ToDomain.kt
index 96b9cab684..97a28d9605 100644
--- a/android/lib/grpc/src/main/kotlin/net/mullvad/mullvadvpn/lib/grpc/mapper/ToDomain.kt
+++ b/android/lib/grpc/src/main/kotlin/net/mullvad/mullvadvpn/lib/grpc/mapper/ToDomain.kt
@@ -23,7 +23,6 @@ import net.mullvad.mullvadvpn.lib.model.ApiAccessMethod
import net.mullvad.mullvadvpn.lib.model.ApiAccessMethodId
import net.mullvad.mullvadvpn.lib.model.ApiAccessMethodName
import net.mullvad.mullvadvpn.lib.model.ApiAccessMethodSetting
-import net.mullvad.mullvadvpn.lib.model.AppId
import net.mullvad.mullvadvpn.lib.model.AppVersionInfo
import net.mullvad.mullvadvpn.lib.model.AuthFailedError
import net.mullvad.mullvadvpn.lib.model.Cipher
@@ -54,6 +53,7 @@ import net.mullvad.mullvadvpn.lib.model.ObfuscationMode
import net.mullvad.mullvadvpn.lib.model.ObfuscationSettings
import net.mullvad.mullvadvpn.lib.model.ObfuscationType
import net.mullvad.mullvadvpn.lib.model.Ownership
+import net.mullvad.mullvadvpn.lib.model.PackageName
import net.mullvad.mullvadvpn.lib.model.ParameterGenerationError
import net.mullvad.mullvadvpn.lib.model.PlayExternalObfuscatedAccountId
import net.mullvad.mullvadvpn.lib.model.Port
@@ -667,7 +667,7 @@ internal fun ManagementInterface.VoucherSubmission.toDomain(): RedeemVoucherSucc
internal fun ManagementInterface.SplitTunnelSettings.toDomain(): SplitTunnelSettings =
SplitTunnelSettings(
enabled = enableExclusions,
- excludedApps = appsList.map { AppId(it) }.toSet(),
+ excludedApps = appsList.map { PackageName(it) }.toSet(),
)
internal fun ManagementInterface.PlayExternalObfuscatedAccountId.toDomain():
diff --git a/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/AppId.kt b/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/AppId.kt
deleted file mode 100644
index 0663b530a1..0000000000
--- a/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/AppId.kt
+++ /dev/null
@@ -1,3 +0,0 @@
-package net.mullvad.mullvadvpn.lib.model
-
-@JvmInline value class AppId(val value: String)
diff --git a/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/PackageName.kt b/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/PackageName.kt
new file mode 100644
index 0000000000..4541c0645a
--- /dev/null
+++ b/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/PackageName.kt
@@ -0,0 +1,3 @@
+package net.mullvad.mullvadvpn.lib.model
+
+@JvmInline value class PackageName(val value: String)
diff --git a/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/SplitTunnelSettings.kt b/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/SplitTunnelSettings.kt
index a937d53bae..b74b79393d 100644
--- a/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/SplitTunnelSettings.kt
+++ b/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/SplitTunnelSettings.kt
@@ -1,3 +1,3 @@
package net.mullvad.mullvadvpn.lib.model
-data class SplitTunnelSettings(val enabled: Boolean, val excludedApps: Set<AppId>)
+data class SplitTunnelSettings(val enabled: Boolean, val excludedApps: Set<PackageName>)
diff --git a/android/lib/repository/src/main/kotlin/net/mullvad/mullvadvpn/lib/repository/SplitTunnelingRepository.kt b/android/lib/repository/src/main/kotlin/net/mullvad/mullvadvpn/lib/repository/SplitTunnelingRepository.kt
index 36442dff85..ed7cc8c47d 100644
--- a/android/lib/repository/src/main/kotlin/net/mullvad/mullvadvpn/lib/repository/SplitTunnelingRepository.kt
+++ b/android/lib/repository/src/main/kotlin/net/mullvad/mullvadvpn/lib/repository/SplitTunnelingRepository.kt
@@ -7,7 +7,7 @@ import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import net.mullvad.mullvadvpn.lib.grpc.ManagementService
-import net.mullvad.mullvadvpn.lib.model.AppId
+import net.mullvad.mullvadvpn.lib.model.PackageName
class SplitTunnelingRepository(
private val managementService: ManagementService,
@@ -26,7 +26,7 @@ class SplitTunnelingRepository(
suspend fun enableSplitTunneling(enabled: Boolean) =
managementService.setSplitTunnelingState(enabled)
- suspend fun excludeApp(app: AppId) = managementService.addSplitTunnelingApp(app)
+ suspend fun excludeApp(app: PackageName) = managementService.addSplitTunnelingApp(app)
- suspend fun includeApp(app: AppId) = managementService.removeSplitTunnelingApp(app)
+ suspend fun includeApp(app: PackageName) = managementService.removeSplitTunnelingApp(app)
}
diff --git a/android/lib/repository/src/main/kotlin/net/mullvad/mullvadvpn/lib/repository/UserPreferencesRepository.kt b/android/lib/repository/src/main/kotlin/net/mullvad/mullvadvpn/lib/repository/UserPreferencesRepository.kt
index 0e851ea3bb..16866d88f8 100644
--- a/android/lib/repository/src/main/kotlin/net/mullvad/mullvadvpn/lib/repository/UserPreferencesRepository.kt
+++ b/android/lib/repository/src/main/kotlin/net/mullvad/mullvadvpn/lib/repository/UserPreferencesRepository.kt
@@ -64,4 +64,13 @@ class UserPreferencesRepository(
userPreferencesStore.updateData { prefs ->
prefs.toBuilder().setShowAndroid16ConnectWarning(show).build()
}
+
+ fun showSystemAppsSplitTunneling(): Flow<Boolean> =
+ userPreferencesStore.data.map { it.showSystemAppsSplitTunneling }
+
+ suspend fun setShowSystemAppsSplitTunneling(show: Boolean) {
+ userPreferencesStore.updateData { prefs ->
+ prefs.toBuilder().setShowSystemAppsSplitTunneling(show).build()
+ }
+ }
}
diff --git a/android/lib/repository/src/main/proto/user_prefs.proto b/android/lib/repository/src/main/proto/user_prefs.proto
index 36db3f2ad3..2d7f21fcd9 100644
--- a/android/lib/repository/src/main/proto/user_prefs.proto
+++ b/android/lib/repository/src/main/proto/user_prefs.proto
@@ -9,4 +9,5 @@ message UserPreferences {
int64 account_expiry_unix_time_seconds = 3;
bool show_android_16_connect_warning = 4;
bool show_location_in_system_notification = 5;
+ bool show_system_apps_split_tunneling = 6;
}
diff --git a/android/lib/ui/component/src/main/kotlin/net/mullvad/mullvadvpn/lib/ui/component/MullvadSearchBar.kt b/android/lib/ui/component/src/main/kotlin/net/mullvad/mullvadvpn/lib/ui/component/MullvadSearchBar.kt
new file mode 100644
index 0000000000..bafe11a38c
--- /dev/null
+++ b/android/lib/ui/component/src/main/kotlin/net/mullvad/mullvadvpn/lib/ui/component/MullvadSearchBar.kt
@@ -0,0 +1,67 @@
+package net.mullvad.mullvadvpn.lib.ui.component
+
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.automirrored.rounded.ArrowBack
+import androidx.compose.material.icons.rounded.Close
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.SearchBarDefaults
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
+import net.mullvad.mullvadvpn.lib.ui.component.textfield.mullvadDarkTextFieldColors
+import net.mullvad.mullvadvpn.lib.ui.theme.Dimens
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun MullvadSearchBar(
+ searchTerm: String,
+ enabled: Boolean,
+ onSearchInputChanged: (String) -> Unit,
+ hideKeyboard: () -> Unit,
+ onGoBack: () -> Unit,
+ modifier: Modifier = Modifier,
+) {
+ SearchBarDefaults.InputField(
+ modifier = modifier.height(Dimens.searchFieldHeightExpanded).fillMaxWidth(),
+ query = searchTerm,
+ enabled = enabled,
+ onQueryChange = onSearchInputChanged,
+ onSearch = { hideKeyboard() },
+ expanded = true,
+ onExpandedChange = {},
+ leadingIcon = {
+ IconButton(onClick = onGoBack) {
+ Icon(
+ imageVector = Icons.AutoMirrored.Rounded.ArrowBack,
+ contentDescription = stringResource(R.string.back),
+ )
+ }
+ },
+ trailingIcon = {
+ if (searchTerm.isNotEmpty()) {
+ IconButton(onClick = { onSearchInputChanged("") }) {
+ Icon(
+ imageVector = Icons.Rounded.Close,
+ contentDescription = stringResource(R.string.clear_input),
+ )
+ }
+ }
+ },
+ placeholder = { Text(text = stringResource(id = R.string.search_placeholder)) },
+ colors =
+ mullvadDarkTextFieldColors()
+ .copy(
+ focusedContainerColor = MaterialTheme.colorScheme.surface,
+ unfocusedContainerColor = MaterialTheme.colorScheme.surface,
+ errorContainerColor = MaterialTheme.colorScheme.surface,
+ disabledContainerColor = MaterialTheme.colorScheme.surface,
+ disabledLeadingIconColor = MaterialTheme.colorScheme.onSurface,
+ ),
+ )
+}
diff --git a/android/lib/ui/component/src/main/kotlin/net/mullvad/mullvadvpn/lib/ui/component/NavigateButton.kt b/android/lib/ui/component/src/main/kotlin/net/mullvad/mullvadvpn/lib/ui/component/button/NavigateBackButton.kt
index 36c54281bb..d299bb57f9 100644
--- a/android/lib/ui/component/src/main/kotlin/net/mullvad/mullvadvpn/lib/ui/component/NavigateButton.kt
+++ b/android/lib/ui/component/src/main/kotlin/net/mullvad/mullvadvpn/lib/ui/component/button/NavigateBackButton.kt
@@ -1,14 +1,13 @@
-package net.mullvad.mullvadvpn.lib.ui.component
+package net.mullvad.mullvadvpn.lib.ui.component.button
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.rounded.ArrowBack
-import androidx.compose.material.icons.rounded.ArrowDownward
-import androidx.compose.material.icons.rounded.Close
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
+import net.mullvad.mullvadvpn.lib.ui.component.R
@Composable
fun NavigateBackIconButton(
@@ -23,23 +22,3 @@ fun NavigateBackIconButton(
)
}
}
-
-@Composable
-fun NavigateBackDownIconButton(onNavigateBack: () -> Unit) {
- IconButton(onClick = onNavigateBack) {
- Icon(
- imageVector = Icons.Rounded.ArrowDownward,
- contentDescription = stringResource(id = R.string.back),
- )
- }
-}
-
-@Composable
-fun NavigateCloseIconButton(onNavigateClose: () -> Unit) {
- IconButton(onClick = onNavigateClose) {
- Icon(
- imageVector = Icons.Rounded.Close,
- contentDescription = stringResource(id = R.string.close),
- )
- }
-}
diff --git a/android/lib/ui/component/src/main/kotlin/net/mullvad/mullvadvpn/lib/ui/component/button/NavigateCloseButton.kt b/android/lib/ui/component/src/main/kotlin/net/mullvad/mullvadvpn/lib/ui/component/button/NavigateCloseButton.kt
new file mode 100644
index 0000000000..39d372949b
--- /dev/null
+++ b/android/lib/ui/component/src/main/kotlin/net/mullvad/mullvadvpn/lib/ui/component/button/NavigateCloseButton.kt
@@ -0,0 +1,19 @@
+package net.mullvad.mullvadvpn.lib.ui.component.button
+
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.rounded.Close
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.res.stringResource
+import net.mullvad.mullvadvpn.lib.ui.component.R
+
+@Composable
+fun NavigateCloseIconButton(onNavigateClose: () -> Unit) {
+ IconButton(onClick = onNavigateClose) {
+ Icon(
+ imageVector = Icons.Rounded.Close,
+ contentDescription = stringResource(id = R.string.close),
+ )
+ }
+}
diff --git a/android/lib/ui/component/src/main/kotlin/net/mullvad/mullvadvpn/lib/ui/component/button/SearchButton.kt b/android/lib/ui/component/src/main/kotlin/net/mullvad/mullvadvpn/lib/ui/component/button/SearchButton.kt
new file mode 100644
index 0000000000..bdfce90c72
--- /dev/null
+++ b/android/lib/ui/component/src/main/kotlin/net/mullvad/mullvadvpn/lib/ui/component/button/SearchButton.kt
@@ -0,0 +1,30 @@
+package net.mullvad.mullvadvpn.lib.ui.component.button
+
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.rounded.Search
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.tooling.preview.Preview
+import net.mullvad.mullvadvpn.lib.ui.component.preview.PreviewColumn
+import net.mullvad.mullvadvpn.lib.ui.resource.R
+
+@Preview
+@Composable
+private fun PreviewSearchButton() {
+ PreviewColumn {
+ SearchButton(onClick = {})
+ SearchButton(onClick = {}, enabled = false)
+ }
+}
+
+@Composable
+fun SearchButton(onClick: () -> Unit, enabled: Boolean = true) {
+ IconButton(onClick = onClick, enabled = enabled) {
+ Icon(
+ imageVector = Icons.Rounded.Search,
+ contentDescription = stringResource(id = R.string.search),
+ )
+ }
+}
diff --git a/android/lib/ui/resource/src/main/res/values-ar/strings.xml b/android/lib/ui/resource/src/main/res/values-ar/strings.xml
index 92fcc979db..ae6c8515d5 100644
--- a/android/lib/ui/resource/src/main/res/values-ar/strings.xml
+++ b/android/lib/ui/resource/src/main/res/values-ar/strings.xml
@@ -397,7 +397,7 @@
<string name="retry">إعادة المحاولة</string>
<string name="save">حفظ</string>
<string name="search">بحث</string>
- <string name="search_location_empty_text">لم يتم العثور على نتائج للبحث عن \"%1$s\"، يُرجى تجربة بحث مختلف</string>
+ <string name="search_no_matches_for_text">لم يتم العثور على نتائج للبحث عن \"%1$s\"، يُرجى تجربة بحث مختلف</string>
<string name="search_placeholder">ابحث عن...</string>
<string name="select_location">حدِّد الموقع</string>
<string name="send">إرسال</string>
diff --git a/android/lib/ui/resource/src/main/res/values-da/strings.xml b/android/lib/ui/resource/src/main/res/values-da/strings.xml
index 580f8336c0..368b68af32 100644
--- a/android/lib/ui/resource/src/main/res/values-da/strings.xml
+++ b/android/lib/ui/resource/src/main/res/values-da/strings.xml
@@ -397,7 +397,7 @@
<string name="retry">Prøv igen</string>
<string name="save">Gem</string>
<string name="search">Søg</string>
- <string name="search_location_empty_text">Intet resultat for \"%1$s\". Prøv en anden søgning</string>
+ <string name="search_no_matches_for_text">Intet resultat for \"%1$s\". Prøv en anden søgning</string>
<string name="search_placeholder">Søg efter...</string>
<string name="select_location">Vælg placering</string>
<string name="send">Send</string>
diff --git a/android/lib/ui/resource/src/main/res/values-de/strings.xml b/android/lib/ui/resource/src/main/res/values-de/strings.xml
index 33e2c155f0..0d7f019219 100644
--- a/android/lib/ui/resource/src/main/res/values-de/strings.xml
+++ b/android/lib/ui/resource/src/main/res/values-de/strings.xml
@@ -397,7 +397,7 @@
<string name="retry">Erneut versuchen</string>
<string name="save">Speichern</string>
<string name="search">Suche</string>
- <string name="search_location_empty_text">Kein Ergebnis für „%1$s“, bitte versuchen Sie einen anderen Suchbegriff</string>
+ <string name="search_no_matches_for_text">Kein Ergebnis für „%1$s“, bitte versuchen Sie einen anderen Suchbegriff</string>
<string name="search_placeholder">Suchen nach …</string>
<string name="select_location">Ort auswählen</string>
<string name="send">Senden</string>
diff --git a/android/lib/ui/resource/src/main/res/values-es/strings.xml b/android/lib/ui/resource/src/main/res/values-es/strings.xml
index c61cba908f..0b1fe3a96f 100644
--- a/android/lib/ui/resource/src/main/res/values-es/strings.xml
+++ b/android/lib/ui/resource/src/main/res/values-es/strings.xml
@@ -397,7 +397,7 @@
<string name="retry">Reintentar</string>
<string name="save">Guardar</string>
<string name="search">Buscar</string>
- <string name="search_location_empty_text">No hay resultados para «%1$s», intente una búsqueda diferente</string>
+ <string name="search_no_matches_for_text">No hay resultados para «%1$s», intente una búsqueda diferente</string>
<string name="search_placeholder">Buscar...</string>
<string name="select_location">Seleccionar ubicación</string>
<string name="send">Enviar</string>
diff --git a/android/lib/ui/resource/src/main/res/values-fa/strings.xml b/android/lib/ui/resource/src/main/res/values-fa/strings.xml
index bf9bc9a706..c3f9d97fe0 100644
--- a/android/lib/ui/resource/src/main/res/values-fa/strings.xml
+++ b/android/lib/ui/resource/src/main/res/values-fa/strings.xml
@@ -397,7 +397,7 @@
<string name="retry">تلاش دوباره</string>
<string name="save">ذخیره</string>
<string name="search">جستجو</string>
- <string name="search_location_empty_text">هیچ نتیجه‌ای برای «%1$s» یافت نشد، لطفاً جستجوی دیگری انجام دهید</string>
+ <string name="search_no_matches_for_text">هیچ نتیجه‌ای برای «%1$s» یافت نشد، لطفاً جستجوی دیگری انجام دهید</string>
<string name="search_placeholder">جست‌وجو برای...</string>
<string name="select_location">انتخاب مکان</string>
<string name="send">ارسال</string>
diff --git a/android/lib/ui/resource/src/main/res/values-fi/strings.xml b/android/lib/ui/resource/src/main/res/values-fi/strings.xml
index 4d487b4453..4d7485644b 100644
--- a/android/lib/ui/resource/src/main/res/values-fi/strings.xml
+++ b/android/lib/ui/resource/src/main/res/values-fi/strings.xml
@@ -397,7 +397,7 @@
<string name="retry">Yritä uudelleen</string>
<string name="save">Tallenna</string>
<string name="search">Haku</string>
- <string name="search_location_empty_text">Haulle \"%1$s\" ei löytynyt tuloksia. Kokeile toista hakua.</string>
+ <string name="search_no_matches_for_text">Haulle \"%1$s\" ei löytynyt tuloksia. Kokeile toista hakua.</string>
<string name="search_placeholder">Hae...</string>
<string name="select_location">Valitse sijainti</string>
<string name="send">Lähetä</string>
diff --git a/android/lib/ui/resource/src/main/res/values-fr/strings.xml b/android/lib/ui/resource/src/main/res/values-fr/strings.xml
index b728e6d952..2490986f08 100644
--- a/android/lib/ui/resource/src/main/res/values-fr/strings.xml
+++ b/android/lib/ui/resource/src/main/res/values-fr/strings.xml
@@ -397,7 +397,7 @@
<string name="retry">Réessayer</string>
<string name="save">Enregistrer</string>
<string name="search">Recherche</string>
- <string name="search_location_empty_text">Aucun résultat pour « %1$s ». Veuillez essayer une autre recherche</string>
+ <string name="search_no_matches_for_text">Aucun résultat pour « %1$s ». Veuillez essayer une autre recherche</string>
<string name="search_placeholder">Rechercher...</string>
<string name="select_location">Sélectionner une localisation</string>
<string name="send">Envoyer</string>
diff --git a/android/lib/ui/resource/src/main/res/values-it/strings.xml b/android/lib/ui/resource/src/main/res/values-it/strings.xml
index 75f5caa769..aa0df0a0eb 100644
--- a/android/lib/ui/resource/src/main/res/values-it/strings.xml
+++ b/android/lib/ui/resource/src/main/res/values-it/strings.xml
@@ -397,7 +397,7 @@
<string name="retry">Riprova</string>
<string name="save">Salva</string>
<string name="search">Cerca</string>
- <string name="search_location_empty_text">Nessun risultato per \"%1$s\", prova una ricerca diversa</string>
+ <string name="search_no_matches_for_text">Nessun risultato per \"%1$s\", prova una ricerca diversa</string>
<string name="search_placeholder">Cerca...</string>
<string name="select_location">Seleziona posizione</string>
<string name="send">Invia</string>
diff --git a/android/lib/ui/resource/src/main/res/values-ja/strings.xml b/android/lib/ui/resource/src/main/res/values-ja/strings.xml
index 5e88cd394d..988aa51dec 100644
--- a/android/lib/ui/resource/src/main/res/values-ja/strings.xml
+++ b/android/lib/ui/resource/src/main/res/values-ja/strings.xml
@@ -397,7 +397,7 @@
<string name="retry">再試行</string>
<string name="save">保存</string>
<string name="search">検索</string>
- <string name="search_location_empty_text">「%1$s」の結果はありません。別の検索をお試しください。</string>
+ <string name="search_no_matches_for_text">「%1$s」の結果はありません。別の検索をお試しください。</string>
<string name="search_placeholder">検索...</string>
<string name="select_location">場所を選択する</string>
<string name="send">送信</string>
diff --git a/android/lib/ui/resource/src/main/res/values-ko/strings.xml b/android/lib/ui/resource/src/main/res/values-ko/strings.xml
index 6b3737bb30..7e007cfc18 100644
--- a/android/lib/ui/resource/src/main/res/values-ko/strings.xml
+++ b/android/lib/ui/resource/src/main/res/values-ko/strings.xml
@@ -397,7 +397,7 @@
<string name="retry">다시 시도</string>
<string name="save">저장</string>
<string name="search">검색</string>
- <string name="search_location_empty_text">\"%1$s\" 검색 결과가 없습니다. 다른 검색어를 시도하세요</string>
+ <string name="search_no_matches_for_text">\"%1$s\" 검색 결과가 없습니다. 다른 검색어를 시도하세요</string>
<string name="search_placeholder">검색...</string>
<string name="select_location">위치 선택</string>
<string name="send">전송</string>
diff --git a/android/lib/ui/resource/src/main/res/values-my/strings.xml b/android/lib/ui/resource/src/main/res/values-my/strings.xml
index e8e92bf08c..5a5a47f366 100644
--- a/android/lib/ui/resource/src/main/res/values-my/strings.xml
+++ b/android/lib/ui/resource/src/main/res/values-my/strings.xml
@@ -397,7 +397,7 @@
<string name="retry">ပြန်ကြိုးစားကြည့်ရန်</string>
<string name="save">သိမ်းမည်</string>
<string name="search">ရှာဖွေမှု</string>
- <string name="search_location_empty_text">\"%1$s\" အတွက် ရလဒ်မရှိပါ၊ အခြားရှာဖွေမှုတစ်ခုကို စမ်းလုပ်ကြည့်ပါ</string>
+ <string name="search_no_matches_for_text">\"%1$s\" အတွက် ရလဒ်မရှိပါ၊ အခြားရှာဖွေမှုတစ်ခုကို စမ်းလုပ်ကြည့်ပါ</string>
<string name="search_placeholder">ရှာရန်...</string>
<string name="select_location">တည်နေရာ ရွေးရန်</string>
<string name="send">ပို့ရန်</string>
diff --git a/android/lib/ui/resource/src/main/res/values-nb/strings.xml b/android/lib/ui/resource/src/main/res/values-nb/strings.xml
index d892d8d15e..07417d0ffd 100644
--- a/android/lib/ui/resource/src/main/res/values-nb/strings.xml
+++ b/android/lib/ui/resource/src/main/res/values-nb/strings.xml
@@ -397,7 +397,7 @@
<string name="retry">Prøv på nytt</string>
<string name="save">Lagre</string>
<string name="search">Søk</string>
- <string name="search_location_empty_text">Ingen resultater for «%1$s». Prøv et annet søk</string>
+ <string name="search_no_matches_for_text">Ingen resultater for «%1$s». Prøv et annet søk</string>
<string name="search_placeholder">Søk etter ...</string>
<string name="select_location">Velg plassering</string>
<string name="send">Send</string>
diff --git a/android/lib/ui/resource/src/main/res/values-nl/strings.xml b/android/lib/ui/resource/src/main/res/values-nl/strings.xml
index 2df3aed49f..5e6af40ddd 100644
--- a/android/lib/ui/resource/src/main/res/values-nl/strings.xml
+++ b/android/lib/ui/resource/src/main/res/values-nl/strings.xml
@@ -397,7 +397,7 @@
<string name="retry">Opnieuw proberen</string>
<string name="save">Opslaan</string>
<string name="search">Zoeken</string>
- <string name="search_location_empty_text">Geen resultaat voor \"%1$s\", probeer een andere zoekopdracht</string>
+ <string name="search_no_matches_for_text">Geen resultaat voor \"%1$s\", probeer een andere zoekopdracht</string>
<string name="search_placeholder">Zoeken naar...</string>
<string name="select_location">Locatie selecteren</string>
<string name="send">Verzenden</string>
diff --git a/android/lib/ui/resource/src/main/res/values-pl/strings.xml b/android/lib/ui/resource/src/main/res/values-pl/strings.xml
index 724382395d..24b13337e0 100644
--- a/android/lib/ui/resource/src/main/res/values-pl/strings.xml
+++ b/android/lib/ui/resource/src/main/res/values-pl/strings.xml
@@ -397,7 +397,7 @@
<string name="retry">Ponów próbę</string>
<string name="save">Zapisz</string>
<string name="search">Wyszukaj</string>
- <string name="search_location_empty_text">Brak wyników dla hasła „%1$s”, spróbuj innego wyszukiwania</string>
+ <string name="search_no_matches_for_text">Brak wyników dla hasła „%1$s”, spróbuj innego wyszukiwania</string>
<string name="search_placeholder">Wyszukaj...</string>
<string name="select_location">Wybierz lokalizację</string>
<string name="send">Wyślij</string>
diff --git a/android/lib/ui/resource/src/main/res/values-pt/strings.xml b/android/lib/ui/resource/src/main/res/values-pt/strings.xml
index 6c13e9ad8d..d98dfb6704 100644
--- a/android/lib/ui/resource/src/main/res/values-pt/strings.xml
+++ b/android/lib/ui/resource/src/main/res/values-pt/strings.xml
@@ -397,7 +397,7 @@
<string name="retry">Tentar novamente</string>
<string name="save">Guardar</string>
<string name="search">Pesquisar</string>
- <string name="search_location_empty_text">Sem resultados para \"%1$s\". Experimente uma pesquisa diferente</string>
+ <string name="search_no_matches_for_text">Sem resultados para \"%1$s\". Experimente uma pesquisa diferente</string>
<string name="search_placeholder">Pesquisar por...</string>
<string name="select_location">Selecionar localização</string>
<string name="send">Enviar</string>
diff --git a/android/lib/ui/resource/src/main/res/values-ru/strings.xml b/android/lib/ui/resource/src/main/res/values-ru/strings.xml
index 4b5a2a5906..982fb4269d 100644
--- a/android/lib/ui/resource/src/main/res/values-ru/strings.xml
+++ b/android/lib/ui/resource/src/main/res/values-ru/strings.xml
@@ -397,7 +397,7 @@
<string name="retry">Повторить</string>
<string name="save">Сохранить</string>
<string name="search">Поиск</string>
- <string name="search_location_empty_text">По запросу «%1$s» ничего не найдено — попробуйте другой запрос</string>
+ <string name="search_no_matches_for_text">По запросу «%1$s» ничего не найдено — попробуйте другой запрос</string>
<string name="search_placeholder">Поиск...</string>
<string name="select_location">Выбор местоположения</string>
<string name="send">Отправить</string>
diff --git a/android/lib/ui/resource/src/main/res/values-sv/strings.xml b/android/lib/ui/resource/src/main/res/values-sv/strings.xml
index 169f4674fb..7ac9964506 100644
--- a/android/lib/ui/resource/src/main/res/values-sv/strings.xml
+++ b/android/lib/ui/resource/src/main/res/values-sv/strings.xml
@@ -397,7 +397,7 @@
<string name="retry">Försök igen</string>
<string name="save">Spara</string>
<string name="search">Sök</string>
- <string name="search_location_empty_text">Inga resultat hittades för \"%1$s\", försök med en annan sökning</string>
+ <string name="search_no_matches_for_text">Inga resultat hittades för \"%1$s\", försök med en annan sökning</string>
<string name="search_placeholder">Sök efter …</string>
<string name="select_location">Välj plats</string>
<string name="send">Skicka</string>
diff --git a/android/lib/ui/resource/src/main/res/values-th/strings.xml b/android/lib/ui/resource/src/main/res/values-th/strings.xml
index 129e20250d..c6265a168b 100644
--- a/android/lib/ui/resource/src/main/res/values-th/strings.xml
+++ b/android/lib/ui/resource/src/main/res/values-th/strings.xml
@@ -397,7 +397,7 @@
<string name="retry">ลองอีกครั้ง</string>
<string name="save">บันทึก</string>
<string name="search">ค้นหา</string>
- <string name="search_location_empty_text">ไม่พบผลลัพธ์สำหรับ \"%1$s\" โปรดลองใช้คำค้นหาอื่น</string>
+ <string name="search_no_matches_for_text">ไม่พบผลลัพธ์สำหรับ \"%1$s\" โปรดลองใช้คำค้นหาอื่น</string>
<string name="search_placeholder">ค้นหา…</string>
<string name="select_location">เลือกตำแหน่งที่ตั้ง</string>
<string name="send">ส่ง</string>
diff --git a/android/lib/ui/resource/src/main/res/values-tr/strings.xml b/android/lib/ui/resource/src/main/res/values-tr/strings.xml
index 566c86860b..6cfc70f63e 100644
--- a/android/lib/ui/resource/src/main/res/values-tr/strings.xml
+++ b/android/lib/ui/resource/src/main/res/values-tr/strings.xml
@@ -397,7 +397,7 @@
<string name="retry">Tekrar dene</string>
<string name="save">Kaydet</string>
<string name="search">Ara</string>
- <string name="search_location_empty_text">\"%1$s\" için sonuç yok, lütfen farklı bir arama yapın</string>
+ <string name="search_no_matches_for_text">\"%1$s\" için sonuç yok, lütfen farklı bir arama yapın</string>
<string name="search_placeholder">Ara...</string>
<string name="select_location">Konum seçin</string>
<string name="send">Gönder</string>
diff --git a/android/lib/ui/resource/src/main/res/values-uk/strings.xml b/android/lib/ui/resource/src/main/res/values-uk/strings.xml
index 3b31f6271f..4852076682 100644
--- a/android/lib/ui/resource/src/main/res/values-uk/strings.xml
+++ b/android/lib/ui/resource/src/main/res/values-uk/strings.xml
@@ -397,7 +397,7 @@
<string name="retry">Повторити</string>
<string name="save">Зберегти</string>
<string name="search">Пошук</string>
- <string name="search_location_empty_text">За запитом «%1$s» нічого не знайдено, спробуйте інший запит</string>
+ <string name="search_no_matches_for_text">За запитом «%1$s» нічого не знайдено, спробуйте інший запит</string>
<string name="search_placeholder">Пошук…</string>
<string name="select_location">Вибір розташування</string>
<string name="send">Надіслати</string>
diff --git a/android/lib/ui/resource/src/main/res/values-zh-rCN/strings.xml b/android/lib/ui/resource/src/main/res/values-zh-rCN/strings.xml
index 1205e4c7a3..3201f42b6b 100644
--- a/android/lib/ui/resource/src/main/res/values-zh-rCN/strings.xml
+++ b/android/lib/ui/resource/src/main/res/values-zh-rCN/strings.xml
@@ -397,7 +397,7 @@
<string name="retry">重试</string>
<string name="save">保存</string>
<string name="search">搜索</string>
- <string name="search_location_empty_text">“%1$s”无结果,请尝试其他搜索</string>
+ <string name="search_no_matches_for_text">“%1$s”无结果,请尝试其他搜索</string>
<string name="search_placeholder">搜索…</string>
<string name="select_location">选择位置</string>
<string name="send">发送</string>
diff --git a/android/lib/ui/resource/src/main/res/values-zh-rTW/strings.xml b/android/lib/ui/resource/src/main/res/values-zh-rTW/strings.xml
index 73a5a11f8f..395cbd358a 100644
--- a/android/lib/ui/resource/src/main/res/values-zh-rTW/strings.xml
+++ b/android/lib/ui/resource/src/main/res/values-zh-rTW/strings.xml
@@ -397,7 +397,7 @@
<string name="retry">重試</string>
<string name="save">儲存</string>
<string name="search">搜尋</string>
- <string name="search_location_empty_text">找不到「%1$s」的結果,請使改用其他搜尋條件。</string>
+ <string name="search_no_matches_for_text">找不到「%1$s」的結果,請使改用其他搜尋條件。</string>
<string name="search_placeholder">搜尋…</string>
<string name="select_location">選擇位置</string>
<string name="send">傳送</string>
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 82ab85978e..4d92f6c27b 100644
--- a/android/lib/ui/resource/src/main/res/values/strings.xml
+++ b/android/lib/ui/resource/src/main/res/values/strings.xml
@@ -209,7 +209,7 @@
<string name="on">On</string>
<string name="wireguard_port_title">WireGuard port</string>
<string name="search_placeholder">Search for...</string>
- <string name="search_location_empty_text">No result for \"%s\", please try a different search</string>
+ <string name="search_no_matches_for_text">No result for \"%s\", please try a different search</string>
<string name="wireguard_custon_port_title">Custom</string>
<string name="port">Port</string>
<string name="custom_port_dialog_submit">Set port</string>