diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/ConnectScreenTest.kt b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/ConnectScreenTest.kt index be998a37289e..59357d052742 100644 --- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/ConnectScreenTest.kt +++ b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/ConnectScreenTest.kt @@ -168,7 +168,7 @@ class ConnectScreenTest { ) // Assert - onNodeWithText("DISCONNECTED").assertExists() + onNodeWithText("DISCONNECTING...").assertExists() onNodeWithText(mockLocationName).assertExists() onNodeWithText("Disconnect").assertExists() } @@ -310,7 +310,7 @@ class ConnectScreenTest { ) // Assert - onNodeWithText("CONNECTED").assertExists() + onNodeWithText("BLOCKING...").assertExists() onNodeWithText(mockLocationName).assertExists() onNodeWithText("Disconnect").assertExists() onNodeWithText("BLOCKING INTERNET").assertExists() diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/ConnectionStatusText.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/ConnectionStatusText.kt index 535dcf2bb825..a5b1cff000e6 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/ConnectionStatusText.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/ConnectionStatusText.kt @@ -47,8 +47,8 @@ private fun TunnelState.text() = is TunnelState.Disconnected -> textResource(id = R.string.disconnected) is TunnelState.Disconnecting -> when (actionAfterDisconnect) { - ActionAfterDisconnect.Nothing -> textResource(id = R.string.disconnected) - ActionAfterDisconnect.Block -> textResource(id = R.string.connected) + ActionAfterDisconnect.Nothing -> textResource(id = R.string.disconnecting) + ActionAfterDisconnect.Block -> textResource(id = R.string.blocking) ActionAfterDisconnect.Reconnect -> textResource(id = R.string.connecting) } is TunnelState.Error -> diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/ConnectScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/ConnectScreen.kt index 73c5eb2413b2..0ce574d8c1a4 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/ConnectScreen.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/ConnectScreen.kt @@ -12,6 +12,8 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.calculateEndPadding +import androidx.compose.foundation.layout.calculateStartPadding import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height @@ -39,6 +41,7 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.layout import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.platform.LocalUriHandler import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.stringResource @@ -271,7 +274,14 @@ fun ConnectScreen( if (screenHeight < SCREEN_HEIGHT_THRESHOLD) SHORT_SCREEN_INDICATOR_BIAS else TALL_SCREEN_INDICATOR_BIAS - Box(Modifier.padding(it).fillMaxSize()) { + Box( + Modifier.padding( + top = it.calculateTopPadding(), + start = it.calculateStartPadding(LocalLayoutDirection.current), + end = it.calculateEndPadding(LocalLayoutDirection.current), + ) + .fillMaxSize() + ) { MullvadMap(state, indicatorPercentOffset) MullvadCircularProgressIndicatorLarge( @@ -293,22 +303,24 @@ fun ConnectScreen( .testTag(CIRCULAR_PROGRESS_INDICATOR), ) - NotificationBanner( - notification = state.inAppNotification, - isPlayBuild = state.isPlayBuild, - openAppListing = onOpenAppListing, - onClickShowAccount = onManageAccountClick, - onClickDismissNewDevice = onDismissNewDeviceClick, - ) - ConnectionCard( - state = state, - modifier = Modifier.align(Alignment.BottomCenter), - onSwitchLocationClick, - onDisconnectClick, - onReconnectClick, - onCancelClick, - onConnectClick, - ) + Box(modifier = Modifier.fillMaxSize().padding(bottom = it.calculateBottomPadding())) { + NotificationBanner( + notification = state.inAppNotification, + isPlayBuild = state.isPlayBuild, + openAppListing = onOpenAppListing, + onClickShowAccount = onManageAccountClick, + onClickDismissNewDevice = onDismissNewDeviceClick, + ) + ConnectionCard( + state = state, + modifier = Modifier.align(Alignment.BottomCenter), + onSwitchLocationClick = onSwitchLocationClick, + onDisconnectClick = onDisconnectClick, + onReconnectClick = onReconnectClick, + onCancelClick = onCancelClick, + onConnectClick = onConnectClick, + ) + } } } } @@ -365,18 +377,9 @@ private fun ConnectionCard( Shapes.large, colors = CardDefaults.cardColors(containerColor = containerColor.value), ) { - Column( - modifier = - Modifier.padding( - top = Dimens.mediumPadding, - start = Dimens.mediumPadding, - end = Dimens.mediumPadding, - bottom = Dimens.smallPadding, - ) - ) { + Column(modifier = Modifier.padding(all = Dimens.mediumPadding)) { ConnectionCardHeader(state, state.location, expanded) { expanded = !expanded } - Logger.d("Tunnelstate: ${state.tunnelState}, expanded: $expanded") AnimatedContent( (state.tunnelState as? TunnelState.Connected)?.featureIndicators to expanded, modifier = Modifier.weight(1f, fill = false), diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/SettingsScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/SettingsScreen.kt index 6bef82fd9ec5..e0caf038395b 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/SettingsScreen.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/SettingsScreen.kt @@ -241,6 +241,13 @@ private fun DaitaCell(isDaitaEnabled: Boolean, onDaitaClick: () -> Unit) { } ), onCellClicked = onDaitaClick, + bodyView = { + Icon( + imageVector = Icons.Default.ChevronRight, + contentDescription = title, + tint = MaterialTheme.colorScheme.onPrimary, + ) + }, ) } @@ -258,5 +265,12 @@ private fun MultihopCell(isMultihopEnabled: Boolean, onMultihopClick: () -> Unit } ), onCellClicked = onMultihopClick, + bodyView = { + Icon( + imageVector = Icons.Default.ChevronRight, + contentDescription = title, + tint = MaterialTheme.colorScheme.onPrimary, + ) + }, ) } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/location/SearchLocationScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/location/SearchLocationScreen.kt index 737baa6f0c39..be5aa0d306cf 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/location/SearchLocationScreen.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/location/SearchLocationScreen.kt @@ -24,12 +24,15 @@ import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.Text import androidx.compose.material3.TextFieldDefaults import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.focus.FocusRequester +import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalSoftwareKeyboardController @@ -255,7 +258,10 @@ fun SearchLocationScreen( onHideBottomSheet = { locationBottomSheetState = null }, ) Column(modifier = Modifier.padding(it)) { + val focusRequester = remember { FocusRequester() } + LaunchedEffect(Unit) { focusRequester.requestFocus() } SearchBar( + modifier = Modifier.focusRequester(focusRequester), searchTerm = state.searchTerm, backgroundColor = backgroundColor, onBackgroundColor = onBackgroundColor, @@ -325,9 +331,10 @@ private fun SearchBar( onSearchInputChanged: (String) -> Unit, hideKeyboard: () -> Unit, onGoBack: () -> Unit, + modifier: Modifier = Modifier, ) { SearchBarDefaults.InputField( - modifier = Modifier.height(Dimens.searchFieldHeightExpanded).fillMaxWidth(), + modifier = modifier.height(Dimens.searchFieldHeightExpanded).fillMaxWidth(), query = searchTerm, onQueryChange = onSearchInputChanged, onSearch = { hideKeyboard() }, diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/FlowUtils.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/FlowUtils.kt index 0c885989233e..e0fa1d29b44f 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/FlowUtils.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/FlowUtils.kt @@ -5,6 +5,7 @@ package net.mullvad.mullvadvpn.util import kotlinx.coroutines.Deferred import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flow inline fun combine( flow: Flow, @@ -61,3 +62,11 @@ fun Deferred.getOrDefault(default: T) = } catch (e: IllegalStateException) { default } + +fun Flow.withPrev(): Flow> = flow { + var prev: T? = null + collect { curr -> + emit(curr to prev) + prev = curr + } +} diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/ConnectViewModel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/ConnectViewModel.kt index 42838d75d625..5fb08bcc4855 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/ConnectViewModel.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/ConnectViewModel.kt @@ -8,7 +8,6 @@ import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.debounce import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.filterIsInstance import kotlinx.coroutines.flow.map @@ -36,6 +35,7 @@ import net.mullvad.mullvadvpn.usecase.SelectedLocationTitleUseCase import net.mullvad.mullvadvpn.util.combine import net.mullvad.mullvadvpn.util.daysFromNow import net.mullvad.mullvadvpn.util.isSuccess +import net.mullvad.mullvadvpn.util.withPrev @Suppress("LongParameterList") class ConnectViewModel( @@ -62,14 +62,14 @@ class ConnectViewModel( combine( selectedLocationTitleUseCase(), inAppNotificationController.notifications, - connectionProxy.tunnelState, + connectionProxy.tunnelState.withPrev(), lastKnownLocationUseCase.lastKnownDisconnectedLocation, accountRepository.accountData, deviceRepository.deviceState.map { it?.displayName() }, ) { selectedRelayItemTitle, notifications, - tunnelState, + (tunnelState, prevTunnelState), lastKnownDisconnectedLocation, accountData, deviceName -> @@ -80,14 +80,22 @@ class ConnectViewModel( tunnelState.location ?: lastKnownDisconnectedLocation is TunnelState.Connecting -> tunnelState.location is TunnelState.Connected -> tunnelState.location - is TunnelState.Disconnecting -> lastKnownDisconnectedLocation + is TunnelState.Disconnecting -> + when (tunnelState.actionAfterDisconnect) { + ActionAfterDisconnect.Nothing -> lastKnownDisconnectedLocation + ActionAfterDisconnect.Block -> lastKnownDisconnectedLocation + // Keep the previous connected location when reconnecting, after + // this state we will reach Connecting with the new relay + // location + ActionAfterDisconnect.Reconnect -> prevTunnelState?.location() + } is TunnelState.Error -> lastKnownDisconnectedLocation }, selectedRelayItemTitle = selectedRelayItemTitle, tunnelState = tunnelState, showLocation = when (tunnelState) { - is TunnelState.Disconnected -> true + is TunnelState.Disconnected -> tunnelState.location != null is TunnelState.Disconnecting -> { when (tunnelState.actionAfterDisconnect) { ActionAfterDisconnect.Nothing -> false @@ -105,7 +113,6 @@ class ConnectViewModel( isPlayBuild = isPlayBuild, ) } - .debounce(UI_STATE_DEBOUNCE_DURATION_MILLIS) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), ConnectUiState.INITIAL) init { diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/ConnectViewModelTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/ConnectViewModelTest.kt index 0d61b3e300f8..1dab9a456535 100644 --- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/ConnectViewModelTest.kt +++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/ConnectViewModelTest.kt @@ -171,12 +171,11 @@ class ConnectViewModelTest { fun `given RelayListUseCase returns new selectedRelayItem uiState should emit new selectedRelayItem`() = runTest { val selectedRelayItemTitle = "Item" - selectedRelayItemFlow.value = selectedRelayItemTitle - viewModel.uiState.test { assertEquals(ConnectUiState.INITIAL, awaitItem()) - val result = awaitItem() - assertEquals(selectedRelayItemTitle, result.selectedRelayItemTitle) + + selectedRelayItemFlow.value = selectedRelayItemTitle + assertEquals(selectedRelayItemTitle, awaitItem().selectedRelayItemTitle) } } @@ -196,7 +195,6 @@ class ConnectViewModelTest { // Act, Assert viewModel.uiState.test { - assertEquals(ConnectUiState.INITIAL, awaitItem()) tunnelState.emit(TunnelState.Disconnected(null)) // Start of with no location @@ -215,12 +213,7 @@ class ConnectViewModelTest { val locationTestItem = null // Act, Assert - viewModel.uiState.test { - assertEquals(ConnectUiState.INITIAL, awaitItem()) - expectNoEvents() - val result = awaitItem() - assertEquals(locationTestItem, result.location) - } + viewModel.uiState.test { assertEquals(locationTestItem, awaitItem().location) } } @Test @@ -278,15 +271,12 @@ class ConnectViewModelTest { val mockErrorState: ErrorState = mockk() val expectedConnectNotificationState = InAppNotification.TunnelStateError(mockErrorState) - val tunnelStateError = TunnelState.Error(mockErrorState) - notifications.value = listOf(expectedConnectNotificationState) // Act, Assert viewModel.uiState.test { assertEquals(ConnectUiState.INITIAL, awaitItem()) - tunnelState.emit(tunnelStateError) - val result = awaitItem() - assertEquals(expectedConnectNotificationState, result.inAppNotification) + notifications.value = listOf(expectedConnectNotificationState) + assertEquals(expectedConnectNotificationState, awaitItem().inAppNotification) } } @@ -315,7 +305,6 @@ class ConnectViewModelTest { viewModel.uiState.test { awaitItem() outOfTimeViewFlow.value = true - awaitItem() } // Assert @@ -328,12 +317,13 @@ class ConnectViewModelTest { // Arrange val tunnel = TunnelState.Error(mockk(relaxed = true)) val lastKnownLocation: GeoIpLocation = mockk(relaxed = true) - lastKnownLocationFlow.emit(lastKnownLocation) - tunnelState.emit(tunnel) // Act, Assert viewModel.uiState.test { assertEquals(ConnectUiState.INITIAL, awaitItem()) + lastKnownLocationFlow.emit(lastKnownLocation) + tunnelState.emit(tunnel) + awaitItem() val result = awaitItem() assertEquals(lastKnownLocation, result.location) } diff --git a/android/buildSrc/src/main/kotlin/Versions.kt b/android/buildSrc/src/main/kotlin/Versions.kt index ccb6f9b1749b..223331cfc55f 100644 --- a/android/buildSrc/src/main/kotlin/Versions.kt +++ b/android/buildSrc/src/main/kotlin/Versions.kt @@ -5,7 +5,7 @@ object Versions { const val minSdkVersion = 26 const val targetSdkVersion = 35 - const val junitJupiter = "5.11.3" + const val junitJupiter = "5.11.4" const val junit5Android = "1.6.0" - const val junit5Plugin = "1.11.2.0" + const val junit5Plugin = "1.11.3.0" } diff --git a/android/config/dependency-check-suppression.xml b/android/config/dependency-check-suppression.xml index 7257124ef7a4..88020e381b8c 100644 --- a/android/config/dependency-check-suppression.xml +++ b/android/config/dependency-check-suppression.xml @@ -49,7 +49,7 @@ ^pkg:maven/com\.google\.protobuf/protobuf-.*@.*$ CVE-2024-7254 - + diff --git a/android/gradle/libs.versions.toml b/android/gradle/libs.versions.toml index 16a900284e89..3428c8a8b968 100644 --- a/android/gradle/libs.versions.toml +++ b/android/gradle/libs.versions.toml @@ -27,7 +27,7 @@ arrow = "2.0.0" # Compose compose = "1.7.6" -compose-destinations = "2.1.0-beta14" +compose-destinations = "2.1.0-beta15" compose-constraintlayout = "1.1.0" compose-material3 = "1.3.1" @@ -36,23 +36,23 @@ grpc-kotlin = "1.4.1" grpc-kotlin-jar = "1.4.1:jdk8@jar" # Koin -koin = "4.0.0" -koin-compose = "4.0.0" + koin = "4.0.1" +koin-compose = "4.0.1" # Ktor -ktor = "3.0.2" +ktor = "3.0.3" # Kotlin # Bump kotlin and kotlin-ksp together, find matching release here: # https://github.com/google/ksp/releases kotlin = "2.1.0" kotlin-ksp = "2.1.0-1.0.29" -kotlinx = "1.9.0" +kotlinx = "1.10.1" kotlinx-serialization = "2.1.0" # Protobuf protobuf-gradle-plugin = "0.9.4" -protobuf = "4.29.1" +protobuf = "4.29.2" # Misc commonsvalidator = "1.9.0" @@ -65,9 +65,7 @@ konsist = "0.17.3" ktfmt = "0.21.0" leakcanary = "2.14" -# Blocked updating to 1.13.13 due to regression -# https://github.com/mockk/mockk/issues/1308 -mockk = "1.13.12" +mockk = "1.13.14" mockwebserver = "4.12.0" play-publisher = "3.12.1" turbine = "1.2.0" diff --git a/android/gradle/verification-keyring.keys b/android/gradle/verification-keyring.keys index fea553943942..591ea06d9f69 100644 --- a/android/gradle/verification-keyring.keys +++ b/android/gradle/verification-keyring.keys @@ -5,7 +5,6 @@ uid Herve Boutemy sub CB6D56B72FDDF8AA -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQENBFgnlA8BCACVtx3oLXcanfvwtMRwal6pLQ8IVMG9+fr4xGdbSHXCRNbosDa5 agU7WeQMPhusSxJGaA3w7NOdjAwD/LeHADhDPeI6llJg1Fb3EyqH0NZaODKU/Or/ @@ -33,7 +32,6 @@ zFdp+62yJA== pub 02216ED811210DAA sub 8C40458A5F28CF7B -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQGNBGADx6IBDADoHin1LGQ8dhnlhfNCBZ3IyXS2NpR1VjmYtHSlh1hGsPcmHuwo 1mLA6JzXF7NuK3Y52pbTr6vz9bAap8Ysjq/3UJeiDbf7FvmO5xAEVUhrpc7AEY7G @@ -68,7 +66,6 @@ gY4uKjJu0S9RuzG1PVw85w5f6UDZlJ01gGvtT81JFrizhvS9t0HoPbDcDhG5iVE= pub 0315BFB7970A144F sub 7CD1B9BD808646B7 -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQINBFqzjCgBEADfFggdskGls5KqMnhvePTtS4Bn/2t9Rl+Wg3ylXgy4IFd4bnI2 9f82dVM/nobNqAnhOp0wEaAcw+57xBx3rjjKQbrMzUweWeL3uJdTwtPWoyzzsUP0 @@ -111,7 +108,6 @@ y4P0AYrPWZyGzOHjiLTIHwMdWd5PMR+rcKCgREkImXtgPnAZB00dq80s/vZGQQyi pub 0374CF2E8DD1BDFD sub F2E4DE8FA750E060 -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQGiBEmoKU8RBADEN0Q6AuEWEeddjARAzNXcjEx1WfTbLxW5abiiy7zLEht63mhF kBlbyxEIRnHCSrPLUqY5ROWdyey8MJw+bsQn005RZmSvq2rniXz3MpcyAcYPVPWx @@ -142,7 +138,6 @@ JrJYqCD0o2ZFlSyaaO+yKrkAn3IGGwB7ArjBZB5GdaGUAP3/5Luk pub 056ACA74D46000BF sub DECB4AA7ECD68C0E -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQGiBEoo3BYRBACXE2oGRA58Ml6s+kvfk6n/AJ+5OFeRT/Xelco/cpdxOVF5LkRk yd+vR2+F9ldBlH7CSTCmrdZIN3M3zrcWndrk/OQkCxNWVnE/a1li7L3G9nYr011k @@ -166,7 +161,6 @@ AL+qdACgj/xAy648au+K8oNMKTa0DrTDnWAAn1uvM3JssKzUdig+2Nj3uc4TLS2b pub 067091F1549B293F -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQINBGLQN/8BEADI0PTSG1Y/Hn7HALEKDFYchJj3KgCoWZDwmLa7gyz+GIlhUxBw WtjmFsisbaA9GbmAKyys6np1fO0mgiUOmuvZ9d18D21WRHpn4hKolyPoP1f8gvnz @@ -190,7 +184,6 @@ sub 3FF44D37464BBB7E sub 6A0975F8B1127B83 sub 6005789E24E5AD1E -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQGNBFzy4ngBDAC4mz6ELMWjfJ8GZtolq3E96T7qjfp4J9FxGVxdbJxkEDnn6MTg V8zhD7yeSZcUSvwzPiDlB/b4RYnh+5LjzKHTsrtr9ja0SupuCkVGkMGWeHhpIGV9 @@ -382,7 +375,6 @@ B/X6XT3iAD8QsXnzTc+ApKE= pub 075DEF3EF14F0793 sub 9A0E57CE53C8AAED -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQENBFlBpcsBCADSxBg9Q+y4QGozz86UESEpJB8SuqrqEyezwQ8OimdBpQfw02I9 F3+xDcKWgUBEK2BNfY4Nn2XtjxeoX6Pf0rFHJnGOgbz4MpgSnoyNGKUvBdHXBc1D @@ -409,7 +401,6 @@ dASNMKpXnws89MmjpWKE/dv3 pub 083891AD4774845A sub 8118B3BCDB1A5000 -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQINBFu1EwUBEADAXapH49L1Lwt28iK737X/+4bRDE+lkMxehnUZ7QJs5zkFz5Sh 9K2rQO0PpvoMSdadGplFyhKdDP/iEUpzxTTbqMs5UjbJr0MoFfE957Vz59mNf9WY @@ -464,7 +455,6 @@ jR7YX5D8RUnPYZnzIzID+ECD0JeFuyBMZI3y8Zog5w1Ce1wnzA== pub 0A4B343F2A55FDAE sub 26C765C5DBAF2E3E -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQINBFXERtUBEACs0Cu7htUr4AWriw03IYNsKh93IZD4rer3xIZ171CaWjUcmIVx XIiAHyB7RUe7Psg3dRGM2TFV5TtQCvR6N730BEfb9ZUeu7ShSX1rRrmRsKR5bPhM @@ -507,7 +497,6 @@ Uih4lxwIhaDt9NQPFfCqfZEJ6DJfJsbFsWpucJN3Sp3+TqE4aonA/p0= pub 0DA8A5EC02D11EAD sub 71499A87DC1FF84B -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQGiBE3LMfMRBAD90h69D8yyPWaSoAyh2mOOOZ/XH0isuBpDZCWptemlMHgImqdQ 2sXLXYT1bJKmSaMw+yKjp8J/NYk69EbmSK1C2nypLQtWhUmXXd3XVYw6hrG/dGvi @@ -540,7 +529,6 @@ uid ExoQuery sub B149AB255ECE68A1 -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQGNBGRn7f8BDAC6MaHz7fUvvp1ygYqK1wAVQGInueyTyix7ZryFB8AjpbBZ17qq LoIexlYIPi5wSVEqpfiBDS9SAuLNetfYxnfCpYtDda2VWsVKH/HmnRvSLP2GorNW @@ -576,7 +564,6 @@ Q3L0n1au+VI0B5Q+HE4pAvkV6DurLrS3k+fNmQqkkCtVSg== pub 15C71C0A4E0B8EDD sub 891E4C2D471515FE -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQINBFcyNOoBEACj0zTN3GkRNAY3jihHZdGvi70i4R8mUfcQUwWGRsGGlzSwyJfe 20qNOHqwHaxVCAIp4e5paNf9cEKepOv5IqMkmaRdiC2W+BHDxcJgBot/IrC81ube @@ -622,7 +609,6 @@ uid Rafael Winterhalter sub A7E989B0634097AC sub 7999BEFBA1039E8B -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQINBF3Ep5QBEADZfs6o1IpZbZ1qlBkoJ7oWL0vFCcdPUgF/PRFXWKlsuFHVVV/N oZF9SDiCJxfvsVXmI+IHTVMR2SszU2xDF2SlScRfZQwrLhBsDP9nv9N1eGIoA5Ny @@ -704,7 +690,6 @@ sub 7D1BE4480B61E2A7 sub C2148900BCD3C2AF sub CFF46EE3C17E53E9 -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQGNBGBP58sBDADYRZmxLOkqrz0QZ/yESRpv7IeHGLqDE1a8QfFtFb14MJCLSAAS 3nMD6Szi9mEjEqYdJURRcMjbUBhePgbhzGa3FYkjAB8lj6IKbu+ogCwVm1S8+caZ @@ -824,7 +809,6 @@ uid White Source sub C23A3B1DDFE72662 -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQENBE9fGuYBCADb5YMNFaBW1l5PMD6s1VDZHOQVG3sY7ANmjCC3CcNFCaMwwOs+ qYUMdRm0xn+ikcEsncWrVV5eiKxEWFV5oYKwKiK90OVLiD5WX7LrJ3pWl3glQiwc @@ -854,7 +838,6 @@ uid Jeff Davidson sub 1768893AA29C3481 -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQGNBGAu1XEBDADLHRFZXSAiNNlRtyDTuVDBdTimYs1sGkzhtmlDRNzRuUYEtl7/ H30Z5uLncVqB33DVIFIaBMuhwHh5tEGEDotMYqVYFYZXL1lqkDgf3AjzSn1uRHg0 @@ -891,7 +874,6 @@ pub 280D66A55F5316C5 uid Brian Campbell (key for signing maven artifacts) -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQENBFOOGVgBCACiDwUZOc6943aBGUrxikkfUnsyZfHtF9jihYmA1pSgfsye+JxR oG9QWW9+3qx4L/d4ZEqBftTWpsjyrY7NyMaeXtJEjE0vhiWNehgXB1z4XTJ66zCX @@ -910,7 +892,6 @@ uid Jason Robert Dillon (CODE SIGNING KEY) sub 7E48854FB524043B -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQINBFiKZ1YBEACeM6QfSGdIf5m5cMYHccQkYrgfWjoD+eQf7EzmHFKJ5nyi0pfm fp85kTMJzOr397yVa5rHvnzWwdltfUiM+lOLS6QcNvhXTLXx/zawBipv4nATkLAq @@ -957,7 +938,6 @@ uid Charles Lee sub D95ECEC170500D9F -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQGNBFsHC7gBDADlkoJglNVbX9MShcAm6jvS5atCZwWT63gSasObXFxswsJQd1NK qryHNcj9tKBfLbSpMOoHeyyIKDdwdxN+6+N9Hi4hf0j1Ub6deJyI8ace8VERWaxF @@ -995,7 +975,6 @@ uid Ceki Gulcu sub 10DA72CD7FBFA159 -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQENBE+ZO+EBCAC3fZOOuYKthr0GcUge0PH2bh18sbM9XUmPKQz/W15l1NA/2ARS 2gUXM0R+SunMlun9KsqjnojJ2ObVPvbm1Hg/66JSRgR3JWfIpSlJxLicpfu8rCfN @@ -1024,7 +1003,6 @@ uid Thomas Vandahl sub 568C01859FB1189B -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQGiBDUPZgMRBADko/odzH1dYwsxp66EWgI3VrL8M0lgwWQYRvO4UimrxWfJS/Qg X3QPcYtMNQW6oRPXFC/+o39wYCmB5U5dQ25ZeTNtJpJRuQs2lPVz2ZFKz3CC0dL3 @@ -1056,7 +1034,6 @@ uid Markus KARG sub D091C8FFA534EDA2 -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQGNBGTNOPIBDACjeIqMmK4jo8NBVVacAwmqnL6H2/ixU/rPg1WEJSJRQbWu0otK Zrs+0tOVuYsQReW5tYUwI8hclSkdO95NC8bM7rlxcO6JSixsjzf2cOXajOAvuLMj @@ -1092,7 +1069,6 @@ ZgMBzI+v/ingWOyKpKqhpZR/50PHA0o23zw8P1BGeQOlr4kFNA== pub 34918B7D3969D2F5 sub 5CE9BCD2ED28F793 -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQGNBF0vfHYBDADEDPY9ub98c7jQe4yMbPke3A/sxNHnn0WuA9JN880DPs3L7lrv 9VHTOlFXslDNBPYSbgFXH5YlMGg8ZY8bhngjc+Z3dtrCX1cAjUXOnibi7fBFomLB @@ -1129,7 +1105,6 @@ uid Houston Paul Putman IV (CODE SIGNING KEY) sub 641E8602017107B2 -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQINBF8fEbYBEADyl13yAw1GTVtrvIIximySaq5q5vJ+UVRH8C7eDi4Uwq21m6cG drlv5dU+A2dME1W66TENIToERKWY15l3huBSuBJl7S6IrM0lHbD3cBDSW4lGeLeV @@ -1174,7 +1149,6 @@ sRRnrYNxdcnK pub 379CE192D401AB61 sub 0CFE993CDBE1D0A2 -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQINBFTi8JIBEACcN1ucQ1uCOZ1owTELQV/6i4q7NbYdJ5wf7yPYfEugSo3yfbo3 Pw/XEvlnpDZmT155sGNOkteZtZMdcm5XhFbdtquLlrkjAcUGatq5rAt3eLAlvU7u @@ -1219,7 +1193,6 @@ uid Lasse Collin sub 5923A9D358ADF744 -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQINBEzEOZIBEACxg/IuXERlDB48JBWmF4NxNUuuup1IhJAJyFGFSKh3OGAO2Ard sNuRLjANsFXA7m7P5eTFcG+BoHHuAVYmKnI3PPZtHVLnUt4pGItPczQZ2BE1WpcI @@ -1346,7 +1319,6 @@ VoXsnPIDNQ6LJGSfDmsaG/mRgZJEunVLGQFe2nsVqNmQxptLaTzty1Zv2dCOEm5W pub 3A1959EEF8726006 sub D908A43FB7EC07AC -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQINBFu07rsBEADYizNlY0FYNZ6q2wx7AmWLw6PHje55uFhYM8Saqtwg/rm1tl78 j28E/coP2zMFf/ec+zqKsfYi4DMmLZ9ESIngMUOIE7mY0Pp4WN7oYFRtvU0ARWyp @@ -1403,7 +1375,6 @@ uid Carl Mastrangelo sub 9B2A1B698A113AAD -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQINBFzwo60BEACg1rgL5jUtKkFE5DiwqJwxzJyJDH00TBSN6ZT+nXh1UxgC9q2h olF9V+2+LV1Jcmnc946xzIMiWLG33QB0NKVCdU5jNuLahOcViQQjNfGXwNzYoNCR @@ -1447,7 +1418,6 @@ nEq4tzixXZee6daOaBArXoaC1pEN8grIppEyMBaqmVP1GT1+pvY= pub 3B42B015E1C86D65 sub 77735070C0DAAE6E -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQGNBGFAJi8BDADSj4kp0UrMmaUfkjWZW0SbsO7ktXjJCew+SySeoLxKSFukH2aq y266xLgnYqFmNFOr6c4Xj/DSkRLUdhtO7e3ZH6EiI5hFcvu7v/HlF6NtgnIgvLKa @@ -1484,7 +1454,6 @@ uid Drew Hamilton sub 91FCCDE555C64A9F -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQINBF42lroBEACWa+RCajazimveyzyVwzq+1Kj8eiJ1XPJXqvIerGOQ6Tx2qeSM 9AkMcfW7HeN6YW3BR/u2s7xe07D6p6c7UjTmiH1v23ELSk0Ou/BNxiTMdTSly77O @@ -1530,7 +1499,6 @@ uid Tatu Saloranta (cowtowncoder) sub 575D6C921D84AC76 -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQINBGL4BxIBEAC+lX44fd/zrVQPzdKygarBd/X0bBpGakT++Kfk4UBGl3q+wd2G R9puB9R377ds8hU7U3To8sHguUZo6DbD9Gb/is/WajSb9g92z+rMow3KbqfCYqWr @@ -1575,7 +1543,6 @@ LBaslDFjyxMECWr3v7TuEmEmNcD+KwNyACFNuBjEBWeuJZYwCkAkVy8AyitrTMh8 pub 3FAAD2CD5ECBB314 sub 3260CB2DEF74135B -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQINBFhqdSMBEACmveOOsQrTky8b5M+Cq6lbhqRB4+INnfigxr7+EMpswo4AxYuA Op/YG+G7NU5h6EK6Tj2dVfXga90GYFkehtFRZgOUJUGKPU/53upsbnsWS8qjJD8g @@ -1618,7 +1585,6 @@ bCSvsEa2cBXwSbD+0JRfuRvpa8CC4KDFkbU3Nb26dEvWPz+jpC3BnVI= pub 40A3C4432BD7308C sub C0B9C2CC3DD97C16 -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQENBE5zrtcBCADFfU0ugIGUCM44fqPJKrsB3TaDu5EpauvFfYqUfyookzMHSKtB 4YqBSKzBEiZ1rFB/KCn7XJTh5epoCau4DsG4U0XZjsx+esDR4ZtL42LEzeMTuluV @@ -1647,7 +1613,6 @@ uid Gabriel Pedro sub C6B6D52A578C27E3 -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQENBFdVzFUBCADWHdrvRXtz/oNLHmUq9bqMy8xmENkrKBRZsV/BgxNyltIUASqX mNCJLc+SbXnPQjYFwC8Uuk/Mu9UL9CE2Z7M2h70eK6AhUNG3BqBXheOkwORobPVy @@ -1681,7 +1646,6 @@ dh+hAQ== pub 4ECE492B63E38ACF sub 7569B5DA31ECF7BB -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQINBFGP/FABEAC+7JFR/qhFujSJzNooyM1Zrc3Qmreadn8K3/7xZ8QpG0MF/UlT TvlUFnjOnlXIpEaFJ0pHnZmpqYXoQqwMNW+qVspFqYa86gMKy8L2VgWWVuBFLMf4 @@ -1725,7 +1689,6 @@ pub 55C7E5E701832382 uid Andrey Somov (SnakeYAML) -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mI0EVdDLQQEEAJMtYCaTA56YsP5RzQzPvqVTaR2nZ27qRk36blHB9WmXK+NHpGeH PHgq59mLPVueo2/M5k/fFrCe36jHePP31gYpFtueeYDfsofHwod0WhsHyC7JfG8d @@ -1739,7 +1702,6 @@ bT4= pub 5796E91EE6619C69 sub 153E7A3C2B4E5118 -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQINBFri3Q8BEAC90D8TTu6C05m/eq6HbU8gOHFc+2VJriVmnoyODTlEk/LAsT6h BRok7nzY0LpNUzUREjJy/w80YTOjLs25IFhnqA6mq8BGLjFwjhBPA4piCyhW/Elh @@ -1793,7 +1755,6 @@ bW1hDmlBBHiPyQWUgkZrTrJGzpzKLFowpWUoy49YiGUZw983YoHw pub 59A252FB1199D873 sub 92BD2D0B5B21ABA2 -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQENBFUBG7QBCADRWXf0Fw05qRhM4cRnGKlOW1ecue1DCxHAtFwoqmAXyTCO+tI0 MEW5SyXUkX6FsWLl6A2y+KgOs669ogzfQ0rnZMEt4HisRp8wpgk3GWR1/9aKYz/c @@ -1820,7 +1781,6 @@ V3u1xg+t7/QlghTMoJAA0H5G pub 5A456ADE180A42C5 sub C8742A78F5711A6A -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQENBFk1oXsBCAC/D37ctgT2vPdbA0EO41DyZpVheogLVSALy4dfrk4HiD5qwrn1 qDdM+9V45z7C6350inKFxArWC5302Tbbp2/lrxE9Ubx9VFGLnw39Pk7MRC8y8XSD @@ -1856,7 +1816,6 @@ uid Eric Bruneton sub 0440006D577EAE4B -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQENBE7JURcBCADO+9Dc4/JnB+wX+fq+Fr2zUGSPOT6/qjE5kXL4FEbJKsqDSAKG VnbtRrsIUdmNIFQmz71bBDFhRBbrSrkz927k8eUPhYtxE2NmmWSuKgrjF4qviPQv @@ -1886,7 +1845,6 @@ uid Ting-Yuan Huang sub 73F7734B17EC71F4 -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQINBGEVsM0BEADiZwFLiyjeOLeGS0jAso0pOwUigT9PpwQq7JFAuJP2i9C4Eunc J2HWRdMhnAY12C2MVetSwhI/4QID+rIreB7ooC4xv8sz1PIC30t2oSYtXF4w5DYh @@ -1930,7 +1888,6 @@ FFV0JExCODaIKkUa0VvdCKDjNSGcYpUC67cOuvpH pub 6425559C47CC79C4 sub D547B4A01F74AC1E -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQENBE3XFIUBCADcj1zw8m1evCgEMqxgOfl6L8y1tsYWsX7tVPvHEkYlXHrdcpkB fGuWPrauvhBmB9sBkFfxzU98Ilz3Xk9pfISYiaMUk9Mk1ZxsCoYPVhxvOSvk5LgS @@ -1958,7 +1915,6 @@ pub 66B50994442D2D40 uid Square Clippy -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQINBGDoYisBEACqUDZnT4h6ma6XIzdC6KR++uDbR2VKdhCuv0Og/sHEKkm6ZbG0 OFB8tAaQx/WlsoQyf3DlLfUEOGDai875Aqor3fbM+E1hrZbQNfsOySKEE52k7PYe @@ -1981,7 +1937,6 @@ uid Ceki Gulcu sub A1766BE5F812AC2E -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mJMEYvEGpBMFK4EEACMEIwQA6knc/2gtbqDhPh5EzrymR4Hwi1Xf2S0aqMopA1zg IeZzBgSfL+4fEfpXL4eAzvrk29jIXSizDEOgFpw3PW3Om1gASxub4Jo6EQrRgOdd @@ -2002,7 +1957,6 @@ pub 6A65176A0FB1CD0B sub EA8543C570FAF804 sub CA890A5FA09CFD80 -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQINBFgMcBMBEAC/xcIVVOOh+F7S0OTzBlFH34s5fDbi6Zto469tZyW1peyWtXAZ m+2jzFfeTCHaUQO3YjoTy2fPygS4tVD+ew4EAzMG5Uti4kwWZw0PYKz2JO/gl1JY @@ -2071,7 +2025,6 @@ uid Rafael Costa sub B9900D33EA67B95A -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQINBGFD9HABEADSP/6Rn9KyL7suUxWdxNntSCLyEsjWAXd0FHAlf20Ep4WRXoCc 6a3tq1rDvicytmMIrFQzeLkT0x9RpoMw6TlkXFehiBVTJ8o/DODBIEAeJ0ol0K/w @@ -2115,7 +2068,6 @@ s3feM3B7CVr+YVIkL9AfOrWE7zqSdGS4XYYbbvuvcQ== pub 72385FF0AF338D52 sub 458AAC45B5189772 -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQINBEr8kngBEACvK2oDnKTCGQWUEMxCgQPYTTaWVHzaRFZCn8po/DnKMh8llPuU GRdi5O7ChLjsg7qlNJKhi//ZoSnNBdPfT7EGNaKxUO13BVNBvXDiNNbUTWGBY2W7 @@ -2158,7 +2110,6 @@ BemLeqNijrvozak+6IJUvgac pub 7457CA33C3CE9E15 sub ABE9F3126BB741C1 -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQENBFIXyRQBCADe285y3Pu7KzoKyP6wqeNXtvvuwMatAmPm5x/i+S8MlryqzsYa x6twUmXV1yKjjtGrO+9fHvTOWBfSSP+fP9KTaTQYSasoJq2Mw4cQDy1i0zrxNZUw @@ -2196,7 +2147,6 @@ sub 6494C6D6997C215E sub 32EE5355A6BC6E42 sub E88979FB9B30ACF2 -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQINBFcMjNMBEAC6Wr5QuLIFgz1V1EFPlg8ty2TsjQEl4VWftUAqWlMevJFWvYEx BOsOZ6kNFfBfjAxgJNWTkxZrHzDl74R7KW/nUx6X57bpFjUyRaB8F3/NpWKSeIGS @@ -2541,7 +2491,6 @@ uid Ktfmt Team sub FA84183FDD6A6B98 -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQENBF6RvwcBCADIVU7oxOiljoWxNTkZ00PKVwyqhahYpN/4lamULtECCS+HAF+J DsNy/6QCl7lKAGrSyn9dvsI56KEkGvUJfpQrpRlg+uIQDMxS8JF7p9n49DNc8Q88 @@ -2571,7 +2520,6 @@ uid Protobuf Release sub DBC5123E2E98FEFE -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQGNBGSsZCsBDADJZoPoHGJNAB3sn/kFQ3zlj+vZ7OY5aWoH2nL3tHQYZvN/pJRs 8wu4Cw1ApatqLIaur6S6LR+s4xB7HxnMvpiF3NMwr6ZeZBUUTGEJbRgFhY9TqZam @@ -2609,7 +2557,6 @@ uid Rob Manning sub AC9F6F1991913E30 -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQGiBEqXMWkRBACnsxVroe9ojc2AnRn/85KJi/Ntsbku5iJ5z72B6I+VGn/b1Xln kuvRJ41RLG13lKVmHtSTq2pajjmAr9jY5gS8nJ3JUES9bG3yKNN1IDswXExfAUJp @@ -2651,7 +2598,6 @@ PxnB/ZP6BiDIRsXsLtZriE8EGBECAA8FAkqXMWkCGwwFCQPCZwAACgkQeXUttslm pub 7A8860944FAD5F62 sub C189C86B813330C4 -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQINBEvxja8BEADAzZOup1X0B12zJsNsDvXVIwmM6bB+uhEsUHoFTvmsEVwRoZtn i7Q0WSFoY+LDxbvC4Bg1+urCrUrstRJYRyF/pMqPYq/HokRlPjtrli/i3mUSd0zN @@ -2694,7 +2640,6 @@ DrxVxHJIMso7y7QkemJxib8JkfFsaOFye3nvehO6ohGnt42hqvBZWke2E/7xC8ds pub 7C25280EAE63EBE5 sub 926DFB2EDB329089 -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQGiBEPonucRBACtbhYckAoyz1tuSXYX4XiqGa5390gIMcxe2hJ+Ncx9o3zX09Im f8PW27BnMrz7EIydgB2wphhjfK4vkNNtm5ZDWH/zJStsk1Fe7lNuuxs8XorX1+8D @@ -2727,7 +2672,6 @@ sub 72FF58594F983302 sub 0588BC69A286FF16 sub 3967D4EDA591B991 -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQGNBF+TCd4BDACbIA94MfIWL0SpvZwBddXgx36Lp9GYOWNgGoQCWSvk9vaMrLaI rEll0xnoP98CfBQYrVSAmHDMhSLBCjNB3V1Sdz8GRdOG7HUffF7Cqwbm3Fxo3H/h @@ -2807,7 +2751,6 @@ RVj0VJjnjfUEB0eMrIlMQ5Z3Sw== pub 7C7D8456294423BA sub 9842FE565AA0601E -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQGiBEvsZw4RBADH20nX+H1xvMBYmXRj1Aae4dRr6Y6qI7QRWHO6Z7/dxr9bk/NN Yjq5KsVOQxZzloVdtqx75rznT7fZq98g7Nq9IeEtB6k4tnh6XQLhljJMk0a3mzdt @@ -2838,7 +2781,6 @@ On/eScLdx27sje7q3sBENw== pub 7FE9900F412D622E sub AE6B5325E74ED034 -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQENBFnyVlkBCACe8zGkIlDV0dUKmk9PWe2Hw8qM9DdPbtpUOpmUOidGY5svQDL3 eqvHk85TbxqFEe3Qbjjt+R+iApFuXy5kmueXTvwCm7nAU+k/pZtPuzHyhDs3iFFH @@ -2866,7 +2808,6 @@ pub 84E913A8E3A748C0 uid The Legion of the Bouncy Castle Inc. (Maven Repository Artifact Signer) -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQGNBGR/8HUBDADJ+V5VgTXFG4xVI/1r07a/pTXoAQhHyJMkVdFScGARsps07VXI IsYgPsifOFU55E7uRMZPTLAx5F1uxoZAWGtXIz0d4ISKhobFquH8jZe7TnsJBJNV @@ -2888,7 +2829,6 @@ uid Marc Philipp sub 8B2A34A7D4A9B8B3 -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQINBFrKW9IBEACkqUvM7hU1WqOOeb1gZ7pUsRliHuoUvYIrd+hdp+qhPmJ0NG0W YhZK5UtJBmqvtHKRkbwYxUuya9zlBmCfQFf0GpFKJ65JSrPSkZADI3aZ4aUkxIUw @@ -2934,7 +2874,6 @@ uid Jesse Wilson sub 51F5B36C761AA122 -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQINBFoQh54BEADOuivAfgGKc4/zDwx+AwJdctjTT0znL9knRTYG6ediv2Eq+CXm gBM9m5twl+qhUB1NtrdHb4BH49VY9/gHr3JDyo5ewu96qkbeQl4pxW0zmHg/yJx7 @@ -2980,7 +2919,6 @@ uid Gary David Gregory (Code signing key) sub 59BA7BFEAD3D7F94 -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQENBE2kzuwBCACYV+G9yxNkSjAKSji0B5ipMGM74JAL1Ogtcu+993pLHHYsdXri WWXi37x9PLjeHxw63mN26SFyrbMJ4A8erLB03PDjw0DEzAwiu9P2vSvL/RFxGBbk @@ -3011,7 +2949,6 @@ uid Lemon App Dev sub CC3ABCCA46B44E94 -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mDMEZD5QwRYJKwYBBAHaRw8BAQdALe3I9pSJ0+wwICGOboTiP2/nFvMlnENU49yj jGIJlje0JExlbW9uIEFwcCBEZXYgPGlnb3Iud29qZGFAZ21haWwuY29tPrg4BGQ+ @@ -3028,7 +2965,6 @@ uid Eric Anderson (Maven Central) sub F57552EA2A2B5F3F -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQENBFUITeIBCADHIijQBuGmC+Oo/XE5qIXxzZ2cK26uD0tlDqaPhRLWt5RP3EbU b6X8ZLE2AlmawFzU0IqndrCDxSyuo9+ZFQRYT+stf+qHFjtvVQJh2+4L2LpcPrnf @@ -3056,7 +2992,6 @@ F4bF+wJcwQA7BYn2shYG64BjbaU= pub 96FB9DB219F3338D sub 684EB33FB007E676 -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQSuBEwVyy4RDAC9hprQuF4fCPCYdtMlb0Mfb+6G2TqerT1MebLm8/KHCRnPbFLg PwGgcyynLX5R2nXUb6oBZQByDN/Dal0UMuC19KeZX83LTcFE9vr516BMXLXXKmM9 @@ -3111,7 +3046,6 @@ uid Liam Miller-Cushon sub D66472CF54179CC4 -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQINBFKD+PgBEAC8IkWujQlmU0/7+QPZFsc/z/rXgg7BQyo330QK4HeMzeCK6WHa SWzVDM9h6nFDs6Xln6YexbZUjLsxS/a/Ox2i26Qg8B+NghgiratbdJsByRrU/3la @@ -3157,7 +3091,6 @@ uid Eclipse Project for JTA sub B7AE15C15C321C44 -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQINBFuvptoBEACkXayv4g1TlrpPEVbDoiXXtJtHddCNOAPbGeqFxQUQmygLQGOa 4j1d4iBwftfB8YlyVlfrrM8CTfZNtLKxzAKFp2XZjXhidW0VnsC0H03FStdM0SmZ @@ -3239,7 +3172,6 @@ uid Zafar Khaja (Independent Developer) sub E40E551329F1B84D -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQENBFKHsekBCADtYXcvg+CkAftzHDuIjhqlGVWxKHXYjbO5/XjHsgMliovC5iFd R1uhl1Y/hOC54ypr7/TtVqdAOVduV0y6q+AYeuvdz+KUQyx1IRvme+5572EMieqk @@ -3269,7 +3201,6 @@ uid Slawomir Jaranowski sub E3F6790A5A167F5A -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQINBGHDIagBEADpzdCwVjVlHuo8qpu9HtmqNpEW4TB7y6+NX7Q39mj8w+iVskE1 sL0+BOCdP6ZMiQziWbOQ2FxCd3mD0ixZ7v1i7+0jowySPacJbVNaPPECP38gDte4 @@ -3315,7 +3246,6 @@ uid Chris Hegarty (CODE SIGNING KEY) sub 11422582625BA3A1 -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQINBGVfMLcBEADqOXLmSGFoNNogiELwGywVtNspaCCoVaMtu65YJ2MSmB67xI1d wrYljVSoBCviHnLyHazPlGwM8lFQkfHvGb+Jq4DbGm94phtpC7kn81t71eT19XQT @@ -3361,7 +3291,6 @@ uid Stefan Bodewig sub 6366592024774157 -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQGiBDsSIk4RBADSCj6rUjV64tYCGT1DYKYR7GthyWpNdGHSYLbETBcDatAe1dzQ 5NsCgfrlybfyeY+y1lxr3T9bqf6zJWDw/718wff96qmmv1qzexSYtmIrj+h53V82 @@ -3391,7 +3320,6 @@ uid Thomas Neidhart sub 8183E80D264EE073 -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQINBE8YNGIBEADEgcfvs8TL3X2Ql62HJ6SrXWAOoHw5CquJxUQkvBGesIT1Hk24 exiPwrlNE1qUjbVlef1Cwk9ZfwMOpJdfP2MQQbx0nxxqv+JtsoeXUy9bTSvZYBUL @@ -3437,7 +3365,6 @@ uid Tom Denley (scarytom) sub 9C4C23E6FFE405BD -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQENBE+xZxIBCACzKctn4ez8xOC0pGThhAwjYWGkzcwK4HNaC1usHThBFz3/t8JN OqUXRixLyi5wELN6GHlsGVUQS3IfB4JtuhScsieSB8PTree68/knMq6JI08mJqZr @@ -3467,7 +3394,6 @@ uid Square Open Source sub 48E8F055BC4EC9EC -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQENBF8DiGgBCACuhZ0SHyv9rqRP0Sluo2NCTY/txDW25R1dLO+iZVYI4UaXI2zJ ZPpgCh1iXo0uJMBbZEy/43zQbIktrjfOmJRwzCbc0vsqJSaoxoRrqft+oP9KBO2F @@ -3496,7 +3422,6 @@ pub A6EA2E2BF22E0543 uid Tobias Warneke (for development purposes) -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQGNBFJQhigBDADpuhND/VUQwJT0nnJxfjAIur59hyaZZ3Ph/KIgmCneyq7lzYO6 xa1ucH8mqNBVNLLBhs4CjihBddU/ZKTX3WnZyhQKQMZr3Tg+TCNFmAR4/hnZ3NjZ @@ -3515,7 +3440,6 @@ dCBwdXJwb3NlcykgPHQud2FybmVrZUBnbXgubmV0Pg== pub AADF2C18DCF95764 sub F341381ACCCFC192 -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQENBFkQreQBCADLaySdCz86fxlMj53KSYkZTRhZnRr6dhRLFVrVRuIW4JLW2tqu /pkwCNYkT1hvUyEzuoCy166wKzAyucocyCIeOj2GAmCt/oH2IVvvBvouQGyCk/91 @@ -3544,7 +3468,6 @@ uid Punyashloka Biswal sub 7B92B768F9D37337 -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQINBGHu5IUBEAC5appY0S1OLTgUnwbM49Y5Km/pL0SWE1nLwGPQKG/YBpcVaKhE zn1w7/3gtqrfQr811OpMVjrV0LAKh+gPg25m4GIYpqtqgO1u3T7e5Za5dq8f0fAP @@ -3590,7 +3513,6 @@ uid Pierre-Yves Ricau sub 3F7EB3ADB58CF1E0 -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mDMEYlrX6hYJKwYBBAHaRw8BAQdAuMmfV7N5GrH0mrA6JbgOFXi5Qx4V+DN6DiEJ yQWXOcu0JlBpZXJyZS1ZdmVzIFJpY2F1IDxweS5yaWNhdUBnbWFpbC5jb20+uDgE @@ -3607,7 +3529,6 @@ uid ?amonn McManus sub 7892707E9657EBD4 -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQENBFdbSfIBCACrFI0ai/abnV2U2Wa9QQZwGk3Fegc8laiuTKc0GoYdyptd83/H hD5S61ppdkOugBjVTHdgda3xJ7zBZdnwjZvV/TyayQltbh6hU+BMlEolzXLgyvY7 @@ -3637,7 +3558,6 @@ uid Checker Framework (Official Release) sub F1E0D2D65F93C351 -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQENBEyH5dUBCAC1jYC7w/f8Y6de25jyQYkQQiSB6eFl/1zzorR30Dusv/ehhGQf Uqi6yhJtf/56OsKNVCCsYO5uAyIhLaAy4kHxtpoJqL5mRu4OeJu5XaVBAPZdlZ40 @@ -3803,7 +3719,6 @@ uid Jens Reimann sub 2ABF179C7861BA67 -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mDMEZdW6pxYJKwYBBAHaRw8BAQdAB83xJQrwnkVtluLOk5xZXr6kanm3ImFXp6io fGTAp/e0IUplbnMgUmVpbWFubiA8Y3Ryb25AZGVudHJhc3NpLmRlPrg4BGXVuqcS @@ -3820,7 +3735,6 @@ uid IntelliJ IDEA Sign Key sub C9F04E6E2DC4F7F8 -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQENBFKneXIBCACtnX3ZQmPujf6ocvdnhsBheze71DSl34TfebyW2Qt+g9NhMxo4 DaJy+iFNnsaMwLZRr6k/qf+ISE3A4opWAQlbk+Wb5s6DPPA2cHH6W4GdkxtuJzqt @@ -3850,7 +3764,6 @@ uid Claude Brisson sub CA7CE2366FCDE199 -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQENBFf5HggBCADKaT/Jc8gPn9+FlIa9WQQzMUEmcv656B17wE+27lEiCz4G1GI1 YyJSrBau7vV8qHIkChD7ysjMfdXTUeBAmNUgrEA98Qrp4eum/Xg5xf2k90hZq6dO @@ -3878,7 +3791,6 @@ rLn7t3piwid4fiWe5/Q9pYtn0jOsRBGzxQEs2XV/i7EQXT8kcqKGKmZWtUC7b92G pub C727D053C4481CF5 sub 29E792953D515FC5 -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQGNBF8pVB0BDADcwRGpJUDe8eVSlJ0yPQl/CyeYc0RWq2f1seUMQO0xFW1xPIeL IE68D9VdgarA88qDLYesfBqzn57/r/ztj2aLEKt8IRunJzd0w0G2rrgSCZQ8RmzL @@ -3914,7 +3826,6 @@ pub C72D9D97378E9A64 uid Triple-T -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQINBF7cNOsBEACoqMk/klmsWO/RhtPsj9Ak2MLfGyT3GVEDr6IIr5EuWadRMdrP p2Et4psYTKW60M1EIghvA0Tda3xePh9lGjIb6IZRECBuSedT9QgRTdJaX2P1ZCN6 @@ -3937,7 +3848,6 @@ uid Developer 47 Degrees (Arrow Signing Key) sub 5C0C05BAF3CE5633 -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQGNBGFThmkBDAClg5qWKL1pC17ZBo0rrNSEjLNqBhJEy2gigqyD/HkJ3aa7EbYq 6QZCdKSbmFla45PNUflmENXw8BOJzLs8KV3at/Cw3gnTO5Hhtk1VG0DiORjUF9rS @@ -3976,7 +3886,6 @@ uid Pierre Yves Ricau sub B2D8461AB7A7DF27 -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mDMEYo/OhhYJKwYBBAHaRw8BAQdAStj5losyChV0W0clNh6HwDgaGgmypqqVICtN K+Vy0oy0JlBpZXJyZSBZdmVzIFJpY2F1IDxweS5yaWNhdUBnbWFpbC5jb20+uDgE @@ -3993,7 +3902,6 @@ uid Artur Signell sub 8C51FFEA17EBEE72 -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQGiBEqFUGwRBADoqE1SC07ycyTl0/3kovz2/H81+cTuhIrDEMWMF5uk53TC4M8W 1WsfHNSxZ3oobyWONAsX8iR7kxI0r3SdGGh8XxKzf82OFItfpY9qvdIj1vABkEYg @@ -4035,7 +3943,6 @@ cSZDh/g39IRmR1eZAJ99TGWyG+3AY0SgrXjqQcAhhCcRyA== pub CA80D1F0EB6CA4BA sub 5EAB8AD72016DF52 -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQINBF62njsBEADakbaGRfpiftmwO/KncA+vG8cNJzPNEU1HD+o0ReMPO6H5G45b 7gxhZut1Ag1jT/vPbSsTtCloCCy8WF7GtWbjCvvRd8SMP0dlH3vG3rnprXxUCnN4 @@ -4080,7 +3987,6 @@ uid Simone Tripodi (simonetripodi) sub F5604C15C002CC79 -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQGiBEuqRGMRBACBis5psYJVe33ZtVEl8KbmdPWvZ02PZOgn4XxIDl4Gc/ShtuKr 0LYk7jOFeh00hwJWGROllsa18VxEfEZpDCLlOOX9Df0BONcq6ObUyZi1ila0oLpz @@ -4125,7 +4031,6 @@ uid Evgeny Mandrikov (CODE SIGNING KEY) sub C59D5D06CF8D0E01 -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQINBE0NT+kBEAD1hzO+dXStXYJj8M6FBn9fxw+grddjM9rqaEgJ2omSdpZZOPBs DRor7v0Rm23Ec17y/7Dd6oR1CvyAeQwhJvNBaAW4LQmUcvvqep4hfkWDhlRvh/QS @@ -4171,7 +4076,6 @@ uid Sam Pullara sub 5EB7D444901BE0D5 -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQENBE51a3EBCAC72cWYJin1cxqJfeotfZ6zscnsOKTpIVzIE+pljJjUMSte3nuO iZeiBsbOQx7fSdDZPaNh+3aVHmsxRL79fZVcMC8j9vbwOnMfqkrE9M8vcIjXmkzc @@ -4201,7 +4105,6 @@ uid Phil Steitz (For signing Apache distributions) sub A0FC00D356EEF1D4 -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQGiBEEn15ERBADm0iBZ/KwIiEYdLWxMIIZVzwF6My1eLRX+TLvPtOO4wf8zE9zV fAsnMI8uCK3as3OwgqTTsKy7KQSqRyG0zd/rvCmJUEET3zHX1e5e2nYq8p1TzJzk @@ -4228,7 +4131,6 @@ fXU0ijWlrXrpaEXqE1WcxMEM pub CE8B1D1D2530EDC5 sub 7ECBD740FF06AEB5 -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQINBFuX5CkBEADkTgn4nzuq0lWR+7kFGYLKvmPLjes4j2nmygIafUjVbNmD70gY DPpbSP02HxgicM6xSSqzZuBVxpbcffqjMPXf8LkVX4iWKZtyzLpf34yaojigU3qF @@ -4284,7 +4186,6 @@ uid JetBrains Compose Team sub 57CE36BB68F1BC57 -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mDMEYYx3eRYJKwYBBAHaRw8BAQdAV7zh1T+xL7mD2O63rTIvRfQ9kwL2Gvq/Q6PD 9apCe2K0LkpldEJyYWlucyBDb21wb3NlIFRlYW0gPGNvbXBvc2VAamV0YnJhaW5z @@ -4301,7 +4202,6 @@ uid Deanna sub 5199F3DAE89C332D -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQGNBGCtdhoBDADdopjDt4eUNEqLJSw1ZICSR0oq09SOVtJSaSYdF8UiXjBfL1Ds fhTDqSv5pT2a2gLj0OU3tFhWHvINLaKKCjQnHVcFXi2LTxt+XBOjRYkFjHVisbaZ @@ -4337,7 +4237,6 @@ zZsj/fMJ+OIZcAhE7UVae48GpW2kLATxmK01oSzvizIlmN3rVz2EnjOun2iuuEpF pub D364ABAA39A47320 sub 3F606403DCA455C8 -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQINBGH0NlsBEACnLJ3vl/aV+4ytkJ6QSfDFHrwzSo1eEXyuFZ85mLijvgGuaKRr c9/lKed0MuyhLJ7YD752kcFCEIyPbjeqEFsBcgU/RWa1AEfaay4eMLBzLSOwCvhD @@ -4382,7 +4281,6 @@ uid grpc-java-team-sonatype sub 9121AD263441EEDD -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQGNBFrjUQUBDADTMQL/4d9EyVhsO4XBH9wbGWxcEJvsu/HvppN5fY8hpMV0+Cr9 wjAeJ7d9zdFJVB8vPLN7bb5dm6SNyK3KiOugqVgZrQ+ZPTvCCgFbFyEXuZwDiOa1 @@ -4421,7 +4319,6 @@ uid Touchlab sub A947A3FCB1697B4F -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQENBF7H/6gBCACbEuIbxWAfHEYViPqdpwxDYauxsYwk6FgA9sSO1nS95KRwx+Cs X6F8nRGnfLtbo6Ffcp6r58fNi9RvY7ueRGiL0kQd6c5GYx6dH1b91Q1qrdVOeEdj @@ -4451,7 +4348,6 @@ uid Dexmaker Team sub E8D0C72FC5A02B28 -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQINBGAlt80BEACftpFzUCGm2u5sV4UgAysobdqZywkUKP147toek4ULQRYpADig AI9J3BCmHbcApLek1U7vj8geB6T7V0c4ELLFPQ+4lQlCPC8Siv5c2gDaZvoMzTlw @@ -4499,7 +4395,6 @@ sub DD971632B94D35C8 sub 489AF4A51AADC0F9 sub 4B435E1E48A78139 -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQGNBFlARpgBDADH49r8XQ2eVVvfxvR+1zGvUUlEzx3cETJBfPKLzwSHd2jxNDHr H4umf/1Gp0EnW0BZVRPGPx4ahBWZgO1Vo09I/Gr+ExPCyGiZrLaslJN2GMEP/Yx8 @@ -4739,7 +4634,6 @@ uid Jake Wharton sub F3DBCE882C3A01AA -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQENBFsNoY0BCADIvRrJEX3k7UeuT6zt+F4++xH+5Qo7QzdicjFhhyb22PLPyIsI Ema+T4QqiPDegUv8yKKTTBmHNw/vSUHTPX9ZUpglckopuOgdfnuQjTKEOEzrN7V/ @@ -4767,7 +4661,6 @@ u4awI7kDfdqz1V5s5prLGlkoPhACUxRZeznT3Js7NVE= pub E16AB52D79FD224F sub 5A34A5E06B936F93 -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQENBFF/4bYBCADTeOLZiVGNbjlPrwG7UcMl+yXmEqpf9dB1A9cuicH3PWXj0WOb LSzHjzoRvRekEqSUmgoveey1lPuA2qjOUkXY6Kiyx+oLiG0/ObJHUQW2O+tjSQ0R @@ -4796,7 +4689,6 @@ uid Gradle Inc. sub 1B80C80E07BC7190 -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQINBGOtCzoBEAC7hGOPLFnfvQKzCZpJb3QYq8X9OiUL4tVa5mG0lDTeBBiuQCDy Iyhpo8IypllGG6Wxj6ZJbhuHXcnXSu/atmtrnnjARMvDnQ20jX77B+g39ZYuqxgw @@ -4844,7 +4736,6 @@ sub 9351716690874F25 sub 3D5839A2262CBBFB sub 60EB70DDAAC2EC21 -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQGNBF/RX/MBDADSqelDQKobURExWUKALq86yTPMxMasxmDlccKFpk5xjWrryL7z qg4Fnb7IK5fKDtcnTANtOv2hlIli1h131+SmjJdD3qhfly7QoszOpr5izDS+FOCj @@ -4962,7 +4853,6 @@ ryv0wlJfEQU0vgBldED2cvmydAtUu32DbR29bHQ= pub E561F0025AC4B54C sub D5A3EBE0316213C6 -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQENBFdo49gBCAC8YVkUvsbQx4BUdOkgMkEd06VuHeVV/8h5zmZQ7K4rWszV8i3U w5juvyqAnSTWVwGhFkx6v9Gu0Xp47pqs1PmVbvcbQtROMUstuffvdWM+QfWY7ly6 @@ -4991,7 +4881,6 @@ uid Arnaud Giuliani sub CD4021C790364261 -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQINBGAsOHIBEADThzgWwvt1MTi28wsF21qVybrJQPFc+Z90aOK0UHqcJQrzc7DP anRNk10tBEURBUKsywHVo44RGT4JyY2Ocwl0ASi2ovNAgnNdlbTavntrsconcHVu @@ -5036,7 +4925,6 @@ pub E93671C7272B7B3F uid Rolf Lear (JDOM) (Used to sign JDOM Packages) -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQENBFCPD00BCAC4tY8wMQTsCKyII/mMkUDAkXA2cLM47fY1Wn+iohtgtalUdA0v AhGvTdFU6/St35rOKNoyLC7Sy30FBYpAEfMB/x9j/CaQtdtGhaQU0hCvtWGhhS3J @@ -5054,7 +4942,6 @@ uid Martin Grotzke sub E3C50D5485223EBA -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQGiBD648WARBAC12hbEesbhDas6wsOuPuhSAMK0KUUX1Xb1lHHHeEIHtgZKkO7O uZWYnsRqYw32C3vUOsV4Y/4DoPls2SZRH4l9COUQbNyIbdZEK6LqkRDlX3RFsrSf @@ -5080,7 +4967,6 @@ P8JF6ACfTrP/c/CrDQ2tckAUpgp6hxvTiZE= pub EE92349AD86DE446 sub E68665C8F91BDE69 -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQENBGO91akBCADDDpIrW/IohUSJNDu9VOUlnfEOm5VS49uqM0uucLi0BeAhy1Fo P6Yg1cJkcK66DtnUoTM/JJLyDzJRlKnniLrYCkw8ScvtPdA5cQKJTY5ecn+9ouR2 @@ -5107,7 +4993,6 @@ DqhGe5NO7GoCns3XxqjpggME9eCEQooeKHlLCAkX2/XttwVSRlrNsdVb82iKy7E= pub EE9E7DC9D92FC896 sub 3B7272A25F20140F -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQENBE/oyDcBCACgYsHtmWmtUzqyr/JN+orfJaTl2363qiS+NJ1lt2CNxUWOqldc VcIGyjmzokxTRpGdCFmT1Lh/hzZhcDPLjrtxf+f6njIibt80OiEbX39gjwZRIikd @@ -5136,7 +5021,6 @@ uid David Burstr?m sub 28CFDE1EB61BB6AA -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mDMEYknmAxYJKwYBBAHaRw8BAQdA/xS5rgP6lF5fl8l+rJB1UiF+7KKDse0PmKYp TNBH7si0KkRhdmlkIEJ1cnN0csO2bSA8ZGF2aWQuYnVyc3Ryb21AZ21haWwuY29t @@ -5153,7 +5037,6 @@ uid abosch (bintray) sub 504F10B64E007C78 -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQINBFsRgCkBEADaI8lsWsDCfPfT1Vj4h/oY8EoZst/fG9wnMyLvfemLJWGVb20h uRe7vwBHhtczlAeRYtfNzQ1xLKXWNpMfiJNr8PeZVhZyTN7My3JYJZnmQm1MN53l @@ -5199,7 +5082,6 @@ uid Netty Project Bot sub 1C9F436B883DCCF6 -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQINBGAhOxEBEADdB5Jy2sSOndOMCTyk8IFIJYPogjXtN7CnyIlqr4jEB5G87TJf m7OxB95aIVS1vSA5ghCm88N1mKtW6jyYjgLFQbbyD9/X3ShVZjh8B2R4atL93SSK @@ -5246,7 +5128,6 @@ uid Jonathan Hedley sub 6064B04A9DC688E0 -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQGiBEtsF2oRBACcai1CJgjBfgteTh61OuTg4dxFwvLSxXy8uM1ouJw5sMx+OKR9 Uq6pAZ1+NAUckUrha9J6qhQ+WQtaO5PI1Cz2f9rY+FBRx3O+jeTaCgGxM8mGUM5e @@ -5279,7 +5160,6 @@ pub F5C81DE10A0B8ECC uid Andrey Somov (SnakeYAML) -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQGNBGY/odABDADzlZ1BXT0zN3rL+z4HP8r/2xM6zN950fwRimBTOiT6uE8aQSxq 283R/gIgM+yQBGjStLP3k/TsFJ2FCz7sug+7s1RP70ymkshalTRg+9QHBr2MU1Cx @@ -5298,7 +5178,6 @@ aWMuc29tb3ZAZ21haWwuY29tPg== pub F6D4A1D411E9D1AE sub B5CB27F94F97173B -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQENBE89LqsBCAC/C7QToaRF8eZgGOxcvp9aG+mFFCMjaRAb4Mh59OYdmUb6ZjfO 9388HPebGbPNR8SHYs0dBIuWY4ZJ7oUTYPswasL8vB0iPFdyHhvkCca+yk0b8ZBM @@ -5327,7 +5206,6 @@ uid Brent Shaffer sub 3F27C05CFD9C2229 -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mDMEYedT8xYJKwYBBAHaRw8BAQdAGLX9QU2oZI8FYI/VFNE2aSCfcGYiOT9WVsti ECHtIoq0JkJyZW50IFNoYWZmZXIgPGJldHRlcmJyZW50QGdvb2dsZS5jb20+uDgE @@ -5344,7 +5222,6 @@ uid Mattia Tommasone sub C3720DDC2E713B7C -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQGNBF+y5lsBDAC5h0qk+OBAscHc/ac3A9C8ZPohXcTVpsOjds73soUAH+QCKO0y gAUuG/hUUU9xkm9PgTwWOEl2qDDcOFXY+9ykeYNUUcCWfs+JmVRfRod4W5pntaT4 @@ -5380,7 +5257,6 @@ Shma5nGIB6R1N366OBlUvjTs0ggfYypbVA+6WqpzParu/r7S8VozcUwcNZt4Cw== pub FFDE55BE73A2D1ED sub 8990C49B7BE68013 -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQINBGFewZgBEADauMBSYbKoa+A/uKbDFJsK6p7I7ff+DbcIntTzbR2mq/0fTr1Z tylAn/NwizCyU+tsdJ94iVVfTPgZZad+ruOUlCmjrk4K6pOvOP943p5fUb58xJSN @@ -5423,7 +5299,6 @@ uid Jake Wharton sub CF771F914C2A4A73 -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v@RELEASE_NAME@ mQGiBE2fCWARBAC3v9wYo5kmynmVP+43ccamidflSLQjjpsXpSDLPFokGxeuw0OC QJy46m8b5ACoCqRlfwnRRcEHxiSlaBATJA6hi7NRO41R39C62JXsIxNJR16JNQ5k diff --git a/android/gradle/verification-metadata.xml b/android/gradle/verification-metadata.xml index bf6ad40f17da..3d24211f0c9d 100644 --- a/android/gradle/verification-metadata.xml +++ b/android/gradle/verification-metadata.xml @@ -291,14 +291,6 @@ - - - - - - - - @@ -371,14 +363,6 @@ - - - - - - - - @@ -1587,20 +1571,20 @@ - - - + + + - - + + - - - + + + - - + + @@ -1650,14 +1634,6 @@ - - - - - - - - @@ -1687,14 +1663,6 @@ - - - - - - - - @@ -1740,14 +1708,6 @@ - - - - - - - - @@ -1798,14 +1758,6 @@ - - - - - - - - @@ -1827,14 +1779,6 @@ - - - - - - - - @@ -1911,14 +1855,6 @@ - - - - - - - - @@ -2006,14 +1942,6 @@ - - - - - - - - @@ -2083,17 +2011,6 @@ - - - - - - - - - - - @@ -2195,14 +2112,6 @@ - - - - - - - - @@ -2253,14 +2162,6 @@ - - - - - - - - @@ -2274,44 +2175,44 @@ - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + @@ -3263,12 +3164,12 @@ - - - + + + - - + + @@ -3457,43 +3358,43 @@ - - - + + + - - - + + + - - - + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + @@ -3878,12 +3779,12 @@ - - - + + + - - + + @@ -4057,28 +3958,28 @@ - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + @@ -4390,436 +4291,436 @@ - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - + - - - + + + - + - - - + + + - - + + - - - + + + - + - - + + - - + + - - + + - - + + - - + + - - + + - - - + + + - + - - - + + + - - + + - - - + + + - + - - - + + + - - + + - - - + + + - - + + @@ -5213,12 +5114,12 @@ - - - + + + - - + + @@ -5254,92 +5155,84 @@ - - - + + + - - + + - - - + + + - + - - - + + + - + - - - + + + - - + + - - - + + + - - - - - - - - - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + @@ -5392,22 +5285,22 @@ - - - + + + - - + + - - - + + + - - - + + + @@ -5415,9 +5308,9 @@ - - - + + + @@ -5425,9 +5318,9 @@ - - - + + + @@ -5435,9 +5328,9 @@ - - - + + + @@ -5455,11 +5348,6 @@ - - - - - @@ -5470,9 +5358,9 @@ - - - + + + @@ -5485,9 +5373,9 @@ - - - + + + @@ -5505,11 +5393,6 @@ - - - - - @@ -5520,12 +5403,12 @@ - - - + + + - - + + @@ -5536,9 +5419,9 @@ - - - + + + @@ -5546,15 +5429,15 @@ - - - + + + - - + + - - + + @@ -5568,12 +5451,12 @@ - - + + - - + + @@ -5584,9 +5467,9 @@ - - - + + + @@ -5594,12 +5477,12 @@ - - - + + + - - + + @@ -5610,9 +5493,9 @@ - - - + + + @@ -5620,9 +5503,9 @@ - - - + + + @@ -5630,9 +5513,9 @@ - - - + + + @@ -5654,9 +5537,9 @@ - - - + + + @@ -5699,9 +5582,9 @@ - - - + + + @@ -5709,17 +5592,17 @@ - - - + + + - - + + - - - + + + @@ -5732,11 +5615,6 @@ - - - - - @@ -5747,9 +5625,9 @@ - - - + + + @@ -5757,9 +5635,9 @@ - - - + + + @@ -5767,9 +5645,9 @@ - - - + + + @@ -5777,9 +5655,9 @@ - - - + + + @@ -5837,9 +5715,6 @@ - - - @@ -5847,6 +5722,17 @@ + + + + + + + + + + + @@ -5878,11 +5764,6 @@ - - - - - @@ -5893,9 +5774,9 @@ - - - + + + @@ -6013,8 +5894,8 @@ - - + + @@ -6023,9 +5904,9 @@ - - - + + + @@ -6033,9 +5914,9 @@ - - - + + + @@ -6101,6 +5982,14 @@ + + + + + + + + @@ -6141,6 +6030,14 @@ + + + + + + + + @@ -6178,6 +6075,14 @@ + + + + + + + + @@ -6226,6 +6131,14 @@ + + + + + + + + @@ -6243,8 +6156,13 @@ - - + + + + + + + @@ -6448,16 +6366,16 @@ - - - - - + + + + + @@ -6473,12 +6391,12 @@ - - - + + + - - + + @@ -6486,12 +6404,12 @@ - - - + + + - - + + @@ -6499,28 +6417,20 @@ - - - + + + - - + + - - - + + + - - - - - - - - - - + + @@ -6531,20 +6441,28 @@ - - - + + + + + + + + + + + - - + + - - - + + + - - + + @@ -6552,28 +6470,28 @@ - - - + + + - - + + - - - + + + - - + + - - - + + + - - + + diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index 2806e9b58137..3904a1066b26 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -3,8 +3,8 @@ distributionPath=wrapper/dists # Upgrade using gradle and wrapper using: ./gradlew wrapper --gradle-version # Compare wrapper checksum with: # https://services.gradle.org/distributions/gradle-{gradleVersion}-bin.zip.sha256 -distributionSha256Sum=f397b287023acdba1e9f6fc5ea72d22dd63669d59ed4a289a29b1a76eee151c6 -distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip +distributionSha256Sum=7a00d51fb93147819aab76024feece20b6b84e420694101f276be952e08bef03 +distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/NotificationTunnelState.kt b/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/NotificationTunnelState.kt index 3ca573a839ca..4c24c87068d0 100644 --- a/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/NotificationTunnelState.kt +++ b/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/NotificationTunnelState.kt @@ -7,14 +7,14 @@ sealed interface NotificationTunnelState { data object Connected : NotificationTunnelState - data object Reconnecting : NotificationTunnelState + data object Blocking : NotificationTunnelState data object Disconnecting : NotificationTunnelState sealed interface Error : NotificationTunnelState { data object DeviceOffline : Error - data object Blocking : Error + data object Blocked : Error data object VpnPermissionDenied : Error diff --git a/android/lib/resource/src/main/res/values-da/strings.xml b/android/lib/resource/src/main/res/values-da/strings.xml index 28dbb865ed7b..13aaf95f087b 100644 --- a/android/lib/resource/src/main/res/values-da/strings.xml +++ b/android/lib/resource/src/main/res/values-da/strings.xml @@ -287,7 +287,6 @@ Det gør den ved at udføre en ekstra nøgleudveksling ved hjælp af en kvantesikker algoritme og blande resultatet med WireGuards almindelige kryptering. Dette ekstra trin bruger cirka 500 kB trafik, hver gang en ny tunnel etableres. Kvante-modstandsdygtig tunnel Genopret forbindelse - Genopretter forbindelse... Indløs Indløs kupon Fjern diff --git a/android/lib/resource/src/main/res/values-de/strings.xml b/android/lib/resource/src/main/res/values-de/strings.xml index 56e95535ff0b..24001a5e1ee3 100644 --- a/android/lib/resource/src/main/res/values-de/strings.xml +++ b/android/lib/resource/src/main/res/values-de/strings.xml @@ -287,7 +287,6 @@ Dazu wird ein zusätzlicher Schlüsselaustausch mit einem quantensicheren Algorithmus durchgeführt und das Ergebnis mit der regulären Verschlüsselung von WireGuard vermischt. Dieser zusätzliche Schritt verbraucht jedes Mal, wenn ein neuer Tunnel aufgebaut wird, etwa 500 KiB an Datenverkehr. Quantenresistenter Tunnel Erneut verbinden - Wiederherstellen der Verbindung … Einlösen Gutschein einlösen Entfernen diff --git a/android/lib/resource/src/main/res/values-es/strings.xml b/android/lib/resource/src/main/res/values-es/strings.xml index 77d6bf6f0c27..c181585b8e65 100644 --- a/android/lib/resource/src/main/res/values-es/strings.xml +++ b/android/lib/resource/src/main/res/values-es/strings.xml @@ -287,7 +287,6 @@ Lo hace al realizar un intercambio de claves adicional usando un algoritmo cuántico seguro y combinando el resultado en el cifrado normal de WireGuard. Este paso extra utiliza aproximadamente 500 kiB de tráfico cada vez que se establece un nuevo túnel. Túnel con resistencia cuántica Reconectar - Volviendo a establecer la conexión... Canjear Canjear cupón Quitar diff --git a/android/lib/resource/src/main/res/values-fi/strings.xml b/android/lib/resource/src/main/res/values-fi/strings.xml index 0c896026c03c..e61ba4dbb4db 100644 --- a/android/lib/resource/src/main/res/values-fi/strings.xml +++ b/android/lib/resource/src/main/res/values-fi/strings.xml @@ -287,7 +287,6 @@ Tunneli torjuu hyökkäykset suorittamalla ylimääräisen avaimenvaihdon käyttämällä ensin kvanttiturvallista algoritmia, jonka tuloksen se sekoittaa WireGuardin tavalliseen salaukseen. Tämä ylimääräinen vaihe käyttää noin 500 kiB liikennettä joka kerta, kun uusi tunneli luodaan. Kvanttihyökkäyksiä kestävä tunneli Yhdistä uudelleen - Yhdistetään uudelleen... Lunasta Lunasta kuponki Poista diff --git a/android/lib/resource/src/main/res/values-fr/strings.xml b/android/lib/resource/src/main/res/values-fr/strings.xml index b41118246624..57c276a287f2 100644 --- a/android/lib/resource/src/main/res/values-fr/strings.xml +++ b/android/lib/resource/src/main/res/values-fr/strings.xml @@ -287,7 +287,6 @@ Pour ce faire, il effectue un échange de clés supplémentaire à l\'aide d\'un algorithme à sécurité quantique et mélange le résultat au chiffrement habituel de WireGuard. Cette étape supplémentaire utilise environ 500 kiB de trafic chaque fois qu\'un nouveau tunnel est établi. Tunnel résistant aux attaques quantiques Reconnexion - Reconnexion en cours... Échanger Échanger un bon Supprimer diff --git a/android/lib/resource/src/main/res/values-it/strings.xml b/android/lib/resource/src/main/res/values-it/strings.xml index 68506035c1ae..85d399b3f72b 100644 --- a/android/lib/resource/src/main/res/values-it/strings.xml +++ b/android/lib/resource/src/main/res/values-it/strings.xml @@ -287,7 +287,6 @@ L\'operazione viene effettuata eseguendo uno scambio di chiavi aggiuntivo con un algoritmo di sicurezza quantistica e mescolando il risultato nella normale crittografia di WireGuard. Questo passaggio aggiuntivo utilizza circa 500 kiB di traffico ogni volta che viene stabilito un nuovo tunnel. Tunnel resistente agli attacchi quantistici Riconnetti - Riconnessione... Riscatta Riscatta voucher Rimuovi diff --git a/android/lib/resource/src/main/res/values-ja/strings.xml b/android/lib/resource/src/main/res/values-ja/strings.xml index c9d076909f15..e3922b12e982 100644 --- a/android/lib/resource/src/main/res/values-ja/strings.xml +++ b/android/lib/resource/src/main/res/values-ja/strings.xml @@ -287,7 +287,6 @@ 耐量子アルゴリズムで追加の鍵の交換を実行し、結果をWireGuardの通常の暗号化に混合させることで行われます。この追加ステップでは、新しいトンネルが確立されるたびに約500kiBのトラフィックが使用されます。 耐量子トンネル 再接続 - 再接続中… 使用する バウチャーを使用する 削除 diff --git a/android/lib/resource/src/main/res/values-ko/strings.xml b/android/lib/resource/src/main/res/values-ko/strings.xml index 694fdf634bc6..09f899d08365 100644 --- a/android/lib/resource/src/main/res/values-ko/strings.xml +++ b/android/lib/resource/src/main/res/values-ko/strings.xml @@ -287,7 +287,6 @@ 이를 위해 양자 안전 알고리즘을 사용하여 추가 키 교환을 수행하고 결과를 WireGuard의 일반 암호화에 혼합하는 방법이 이용됩니다. 이 추가 단계는 새 터널이 설정될 때마다 약 500kiB의 트래픽을 사용합니다. 양자 저항 터널 다시 연결 - 다시 연결 중... 사용 바우처 사용 제거 diff --git a/android/lib/resource/src/main/res/values-my/strings.xml b/android/lib/resource/src/main/res/values-my/strings.xml index 372ece1f6163..789af576cfaf 100644 --- a/android/lib/resource/src/main/res/values-my/strings.xml +++ b/android/lib/resource/src/main/res/values-my/strings.xml @@ -287,7 +287,6 @@ Quantum Safe အယ်လဂိုရီသမ်တစ်ခုကို သုံး၍ ထပ်ဆောင်း ကီးဖလှယ်မှုတစ်ခုကို ဆောင်ရွက်ပြီး WireGuard ၏ ပုံမှန် ကုဒ်ပြောင်းဝှက်မှုအတွင်း ရလဒ်ကို ရောနှောခြင်းအားဖြင့် ဤသည်ကို လုပ်ဆောင်ပါသည်။ ဤထပ်ဆောင်းအဆင့်သည် Tunnel အသစ်တစ်ခု တည်ဆောက်တိုင်း ဒေတာ 500 kiB ခန့်ကို သုံးပါသည်။ Quantum-resistant Tunnel ပြန်ချိတ်ဆက်ရန် - ပြန်ချိတ်ဆက်နေသည်... လဲယူရန် ဘောက်ချာဖြင့် လဲယူရန် ဖယ်ရှားရန် diff --git a/android/lib/resource/src/main/res/values-nb/strings.xml b/android/lib/resource/src/main/res/values-nb/strings.xml index 9c358bb4a9ca..e842f655a61a 100644 --- a/android/lib/resource/src/main/res/values-nb/strings.xml +++ b/android/lib/resource/src/main/res/values-nb/strings.xml @@ -287,7 +287,6 @@ Det gjøres ved at å utføre en ekstra nøkkelutveksling med en kvantesikker algoritme og kombinere resultatet med WireGuard sin vanlige kryptering. Dette ekstratrinnet bruker omtrent 500 kiB trafikk hver gang det opprettes en ny tunnel. Kvantebestandig tunnel Koble til på nytt - Kobler til på nytt ... Løs inn Løs inn kupong Fjern diff --git a/android/lib/resource/src/main/res/values-nl/strings.xml b/android/lib/resource/src/main/res/values-nl/strings.xml index baf0ea8cd7d6..a7a5732edacf 100644 --- a/android/lib/resource/src/main/res/values-nl/strings.xml +++ b/android/lib/resource/src/main/res/values-nl/strings.xml @@ -287,7 +287,6 @@ Het doet dit door een extra sleuteluitwisseling uit te voeren met een kwantumveilig algoritme en het resultaat te mengen met de reguliere versleuteling van WireGuard. Deze extra stap gebruikt ongeveer 500 kiB aan verkeer elke keer dat een nieuwe tunnel wordt opgezet. Kwantumbestendige tunnel Opnieuw verbinden - Opnieuw verbinden... Inwisselen Voucher inwisselen Verwijderen diff --git a/android/lib/resource/src/main/res/values-pl/strings.xml b/android/lib/resource/src/main/res/values-pl/strings.xml index c28ff45293ce..80fa36e5bd13 100644 --- a/android/lib/resource/src/main/res/values-pl/strings.xml +++ b/android/lib/resource/src/main/res/values-pl/strings.xml @@ -287,7 +287,6 @@ Jest to wykonywane poprzez dodatkową wymianę kluczy przy użyciu algorytmu odpornego na ataki z użyciem komputerów kwantowych i zmieszanie wyniku ze zwykłym szyfrowaniem WireGuard. Ten dodatkowy krok zużywa około 500 kB ruchu za każdym razem, gdy ustanawiany jest nowy tunel. Tunel odporny na ataki z użyciem komputerów kwantowych Połącz ponownie - Ponowne łączenie... Zrealizuj Zrealizuj kupon Usuń diff --git a/android/lib/resource/src/main/res/values-pt/strings.xml b/android/lib/resource/src/main/res/values-pt/strings.xml index c6f197e89a55..d1c940560cac 100644 --- a/android/lib/resource/src/main/res/values-pt/strings.xml +++ b/android/lib/resource/src/main/res/values-pt/strings.xml @@ -287,7 +287,6 @@ Fá-lo ao realizar uma troca de chaves adicional utilizando um algoritmo de segurança quântica e misturando o resultado na encriptação regular do WireGuard. Este passo adicional utiliza aproximadamente 500 kiB de tráfego sempre que um novo túnel é estabelecido. Túnel com resistência quântica Religar - A religar... Reclamar Reclamar voucher Remover diff --git a/android/lib/resource/src/main/res/values-ru/strings.xml b/android/lib/resource/src/main/res/values-ru/strings.xml index 9699880d972b..b93e21062d8b 100644 --- a/android/lib/resource/src/main/res/values-ru/strings.xml +++ b/android/lib/resource/src/main/res/values-ru/strings.xml @@ -287,7 +287,6 @@ Для этого функция выполняет дополнительный обмен ключами с использованием квантово-устойчивого алгоритма и добавляет результат к обычному шифрованию WireGuard. Эта дополнительная мера использует примерно 500 КиБ трафика при каждом создании нового туннеля. Квантово-устойчивый туннель Переподключить - Идет переподключение... Погасить Погасить ваучер Удалить diff --git a/android/lib/resource/src/main/res/values-sv/strings.xml b/android/lib/resource/src/main/res/values-sv/strings.xml index 1f1f15d1639d..215df1e8e1ec 100644 --- a/android/lib/resource/src/main/res/values-sv/strings.xml +++ b/android/lib/resource/src/main/res/values-sv/strings.xml @@ -287,7 +287,6 @@ Den gör det genom att göra ett extra nyckelutbyte med en kvantsäker algoritm och kombinera resultatet med WireGuards vanliga kryptering. Det här extra steget använder ungefär 500 KiB i trafik varje gång en ny tunnel upprättas. Kvantresistent tunnel Återanslut - Återansluter ... Lös in Lös in kupong Ta bort diff --git a/android/lib/resource/src/main/res/values-th/strings.xml b/android/lib/resource/src/main/res/values-th/strings.xml index f82e49a3e63e..7aafd7be05ce 100644 --- a/android/lib/resource/src/main/res/values-th/strings.xml +++ b/android/lib/resource/src/main/res/values-th/strings.xml @@ -287,7 +287,6 @@ ระบบจะดำเนินการสิ่งนี้ผ่านการแลกเปลี่ยนคีย์เพิ่มเติม โดยการใช้อัลกอริทึมแบบควอนตัมที่ปลอดภัย และผสมผลลัพธ์เข้ากับการเข้ารหัสตามปกติของ WireGuard และขั้นตอนพิเศษนี้ใช้การรับส่งข้อมูลประมาณ 500 kiB ในทุกครั้งที่สร้างช่องทางใหม่ ช่องทางการสกัดกั้นควอนตัม เชื่อมต่อใหม่ - กำลังเชื่อมต่อใหม่... แลกรับ แลกบัตรกำนัล ลบ diff --git a/android/lib/resource/src/main/res/values-tr/strings.xml b/android/lib/resource/src/main/res/values-tr/strings.xml index 32979c266c01..b076839b103d 100644 --- a/android/lib/resource/src/main/res/values-tr/strings.xml +++ b/android/lib/resource/src/main/res/values-tr/strings.xml @@ -287,7 +287,6 @@ Bu işlemi, bir kuantum güvenlik algoritmasıyla ekstra bir anahtar değişimi gerçekleştirdikten sonra sonucu WireGuard\'ın normal şifrelemesiyle karıştırarak yapar. Bu ekstra adım, her yeni tünel kurulduğunda yaklaşık 500 kiB trafik kullanır. Kuantuma dayanıklı tünel Yeniden Bağlan - Yeniden bağlanılıyor... Kullan Kuponu kullan Kaldır diff --git a/android/lib/resource/src/main/res/values-zh-rCN/strings.xml b/android/lib/resource/src/main/res/values-zh-rCN/strings.xml index 6e8f6f87ce63..5d253f81bae5 100644 --- a/android/lib/resource/src/main/res/values-zh-rCN/strings.xml +++ b/android/lib/resource/src/main/res/values-zh-rCN/strings.xml @@ -287,7 +287,6 @@ 实现方法是使用量子安全算法执行额外的密钥交换,并将结果混合到 WireGuard 的常规加密中。每次建立新隧道时,这一额外步骤都会使用约 500 kiB 的流量。 抗量子隧道 重新连接 - 正在重新连接… 兑换 兑换优惠券 移除 diff --git a/android/lib/resource/src/main/res/values-zh-rTW/strings.xml b/android/lib/resource/src/main/res/values-zh-rTW/strings.xml index 0bb60e77b579..15b40db2edde 100644 --- a/android/lib/resource/src/main/res/values-zh-rTW/strings.xml +++ b/android/lib/resource/src/main/res/values-zh-rTW/strings.xml @@ -287,7 +287,6 @@ 實現方法是使用量子安全演算法執行額外的金鑰交換,並將結果混合至 WireGuard 的常規加密。每次建立新通道時,這一額外步驟都會使用約 500 kiB 流量。 抗量子通道 重新連線 - 正在重新連線… 兌換 兌換憑證 移除 diff --git a/android/lib/resource/src/main/res/values/strings.xml b/android/lib/resource/src/main/res/values/strings.xml index c19faafd54d9..49b5481a7ab3 100644 --- a/android/lib/resource/src/main/res/values/strings.xml +++ b/android/lib/resource/src/main/res/values/strings.xml @@ -1,7 +1,7 @@ Connecting... - Reconnecting... Disconnecting... + Blocking... Critical error (your attention is required) VPN tunnel status Shows current VPN tunnel status diff --git a/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/tunnelstate/TunnelStateNotificationAction.kt b/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/tunnelstate/TunnelStateNotificationAction.kt index f836cbcd1b68..2cb11744c127 100644 --- a/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/tunnelstate/TunnelStateNotificationAction.kt +++ b/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/tunnelstate/TunnelStateNotificationAction.kt @@ -48,8 +48,8 @@ private fun NotificationTunnelState.contentTitleResourceId(context: Context): St } } NotificationTunnelState.Disconnecting -> context.getString(R.string.disconnecting) - NotificationTunnelState.Reconnecting -> context.getString(R.string.reconnecting) - NotificationTunnelState.Error.Blocking -> context.getString(R.string.blocking_internet) + NotificationTunnelState.Blocking -> context.getString(R.string.blocking) + NotificationTunnelState.Error.Blocked -> context.getString(R.string.blocking_internet) is NotificationTunnelState.Error.Critical -> context.getString(R.string.critical_error) NotificationTunnelState.Error.DeviceOffline -> context.getString(R.string.blocking_internet_device_offline) diff --git a/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/tunnelstate/TunnelStateNotificationProvider.kt b/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/tunnelstate/TunnelStateNotificationProvider.kt index cb589096d974..0f30f9ee78e4 100644 --- a/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/tunnelstate/TunnelStateNotificationProvider.kt +++ b/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/tunnelstate/TunnelStateNotificationProvider.kt @@ -1,14 +1,9 @@ package net.mullvad.mullvadvpn.service.notifications.tunnelstate import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.distinctUntilChanged -import kotlinx.coroutines.flow.filterIsInstance -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.stateIn import net.mullvad.mullvadvpn.lib.model.ActionAfterDisconnect import net.mullvad.mullvadvpn.lib.model.DeviceState @@ -36,22 +31,16 @@ class TunnelStateNotificationProvider( internal val notificationId = NotificationId(2) override val notifications: StateFlow> = - combine( - connectionProxy.tunnelState, - connectionProxy.tunnelState.actionAfterDisconnect().distinctUntilChanged(), - deviceRepository.deviceState, - ) { tunnelState, actionAfterDisconnect, deviceState -> + combine(connectionProxy.tunnelState, deviceRepository.deviceState) { + tunnelState, + deviceState -> if ( deviceState is DeviceState.LoggedOut && tunnelState is TunnelState.Disconnected ) { return@combine NotificationUpdate.Cancel(notificationId) } val notificationTunnelState = - tunnelState( - tunnelState, - actionAfterDisconnect, - vpnPermissionRepository.invoke().leftOrNull(), - ) + tunnelState(tunnelState, vpnPermissionRepository.invoke().leftOrNull()) return@combine NotificationUpdate.Notify( notificationId, @@ -67,36 +56,19 @@ class TunnelStateNotificationProvider( private fun tunnelState( tunnelState: TunnelState, - actionAfterDisconnect: ActionAfterDisconnect?, prepareError: PrepareError?, - ): NotificationTunnelState = - tunnelState.toNotificationTunnelState(actionAfterDisconnect, prepareError) + ): NotificationTunnelState = tunnelState.toNotificationTunnelState(prepareError) - private fun Flow.actionAfterDisconnect(): Flow = - filterIsInstance() - .map { it.actionAfterDisconnect } - .onStart { emit(null) } - - private fun TunnelState.toNotificationTunnelState( - actionAfterDisconnect: ActionAfterDisconnect?, - prepareError: PrepareError?, - ) = + private fun TunnelState.toNotificationTunnelState(prepareError: PrepareError?) = when (this) { is TunnelState.Disconnected -> NotificationTunnelState.Disconnected(prepareError) - is TunnelState.Connecting -> { - if (actionAfterDisconnect == ActionAfterDisconnect.Reconnect) { - NotificationTunnelState.Reconnecting - } else { - NotificationTunnelState.Connecting - } - } - is TunnelState.Disconnecting -> { - if (actionAfterDisconnect == ActionAfterDisconnect.Reconnect) { - NotificationTunnelState.Reconnecting - } else { - NotificationTunnelState.Disconnecting + is TunnelState.Connecting -> NotificationTunnelState.Connecting + is TunnelState.Disconnecting -> + when (actionAfterDisconnect) { + ActionAfterDisconnect.Reconnect -> NotificationTunnelState.Connecting + ActionAfterDisconnect.Block -> NotificationTunnelState.Blocking + ActionAfterDisconnect.Nothing -> NotificationTunnelState.Disconnecting } - } is TunnelState.Connected -> NotificationTunnelState.Connected is TunnelState.Error -> toNotificationTunnelState() } @@ -106,14 +78,14 @@ class TunnelStateNotificationProvider( return when { cause is ErrorStateCause.IsOffline && errorState.isBlocking -> NotificationTunnelState.Error.DeviceOffline - cause is ErrorStateCause.InvalidDnsServers -> NotificationTunnelState.Error.Blocking + cause is ErrorStateCause.InvalidDnsServers -> NotificationTunnelState.Error.Blocked cause is ErrorStateCause.OtherLegacyAlwaysOnApp -> NotificationTunnelState.Error.LegacyLockdown cause is ErrorStateCause.NotPrepared -> NotificationTunnelState.Error.VpnPermissionDenied cause is ErrorStateCause.OtherAlwaysOnApp -> NotificationTunnelState.Error.AlwaysOnVpn(cause.appName) - errorState.isBlocking -> NotificationTunnelState.Error.Blocking + errorState.isBlocking -> NotificationTunnelState.Error.Blocked else -> NotificationTunnelState.Error.Critical } } @@ -129,12 +101,12 @@ class TunnelStateNotificationProvider( } } NotificationTunnelState.Disconnecting -> NotificationAction.Tunnel.Connect - NotificationTunnelState.Connected, - NotificationTunnelState.Error.Blocking -> NotificationAction.Tunnel.Disconnect + NotificationTunnelState.Error.Blocked, + NotificationTunnelState.Blocking, + NotificationTunnelState.Error.DeviceOffline, + NotificationTunnelState.Connected -> NotificationAction.Tunnel.Disconnect NotificationTunnelState.Connecting -> NotificationAction.Tunnel.Cancel - NotificationTunnelState.Reconnecting -> NotificationAction.Tunnel.Cancel is NotificationTunnelState.Error.Critical, - NotificationTunnelState.Error.DeviceOffline, NotificationTunnelState.Error.VpnPermissionDenied, is NotificationTunnelState.Error.AlwaysOnVpn, NotificationTunnelState.Error.LegacyLockdown -> NotificationAction.Tunnel.Dismiss diff --git a/desktop/packages/mullvad-vpn/locales/messages.pot b/desktop/packages/mullvad-vpn/locales/messages.pot index 2f18fac26b4c..2a50198bf099 100644 --- a/desktop/packages/mullvad-vpn/locales/messages.pot +++ b/desktop/packages/mullvad-vpn/locales/messages.pot @@ -2361,6 +2361,9 @@ msgstr "" msgid "Blocking internet (device offline)" msgstr "" +msgid "Blocking..." +msgstr "" + msgid "Changelog" msgstr "" @@ -2598,9 +2601,6 @@ msgstr "" msgid "Privacy policy" msgstr "" -msgid "Reconnecting..." -msgstr "" - msgid "Recursion limit" msgstr "" diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/ApiAccessMethods.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/ApiAccessMethods.tsx index af194e5ea828..bf1a2025df0e 100644 --- a/desktop/packages/mullvad-vpn/src/renderer/components/ApiAccessMethods.tsx +++ b/desktop/packages/mullvad-vpn/src/renderer/components/ApiAccessMethods.tsx @@ -7,7 +7,7 @@ import { AccessMethodSetting } from '../../shared/daemon-rpc-types'; import { messages } from '../../shared/gettext'; import { useAppContext } from '../context'; import { useApiAccessMethodTest } from '../lib/api-access-methods'; -import { Flex } from '../lib/components'; +import { Container, Flex } from '../lib/components'; import { Spacings } from '../lib/foundations'; import { useHistory } from '../lib/history'; import { generateRoutePath } from '../lib/routeHelpers'; @@ -24,13 +24,7 @@ import { import ImageView from './ImageView'; import InfoButton from './InfoButton'; import { BackAction } from './KeyboardNavigation'; -import { - Layout, - SettingsContainer, - SettingsContent, - SettingsNavigationScrollbars, - SettingsStack, -} from './Layout'; +import { Layout, SettingsContainer, SettingsContent, SettingsNavigationScrollbars } from './Layout'; import { ModalAlert, ModalAlertType } from './Modal'; import { NavigationBar, @@ -40,7 +34,7 @@ import { TitleBarItem, } from './NavigationBar'; import SettingsHeader, { HeaderSubTitle, HeaderTitle } from './SettingsHeader'; -import { SmallButton, SmallButtonColor, SmallButtonGroup } from './SmallButton'; +import { SmallButton, SmallButtonColor } from './SmallButton'; const StyledContextMenuButton = styled(Cell.Icon)({ alignItems: 'center', @@ -127,7 +121,7 @@ export default function ApiAccessMethods() { - + ))} - - + {messages.gettext('Add')} - - + + diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/SplitTunnelingSettings.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/SplitTunnelingSettings.tsx index a24790e66a5b..cab859b8c97a 100644 --- a/desktop/packages/mullvad-vpn/src/renderer/components/SplitTunnelingSettings.tsx +++ b/desktop/packages/mullvad-vpn/src/renderer/components/SplitTunnelingSettings.tsx @@ -10,7 +10,7 @@ import { } from '../../shared/application-types'; import { messages } from '../../shared/gettext'; import { useAppContext } from '../context'; -import { Flex, FootnoteMini } from '../lib/components'; +import { Button, Container, Flex, FootnoteMini } from '../lib/components'; import { Colors, Spacings } from '../lib/foundations'; import { useHistory } from '../lib/history'; import { formatHtml } from '../lib/html-formatter'; @@ -19,6 +19,7 @@ import { IReduxState } from '../redux/store'; import Accordion from './Accordion'; import * as AppButton from './AppButton'; import * as Cell from './cell'; +import { measurements } from './common-styles'; import { CustomScrollbarsRef } from './CustomScrollbars'; import ImageView from './ImageView'; import { BackAction } from './KeyboardNavigation'; @@ -495,42 +496,54 @@ export function SplitTunnelingSettings(props: IPlatformSplitTunnelingSettingsPro )} - - - - - - - - - - - - - {canEditSplitTunneling && searchTerm !== '' && !showSplitSection && !showNonSplitSection && ( - - - {formatHtml( - sprintf(messages.gettext('No result for %(searchTerm)s.'), { searchTerm }), - )} - - {messages.gettext('Try a different search.')} - - )} + + + + + + + + + + + + + + - {canEditSplitTunneling && ( - - {messages.pgettext('split-tunneling-view', 'Find another app')} - - )} + {canEditSplitTunneling && + searchTerm !== '' && + !showSplitSection && + !showNonSplitSection && ( + + + {formatHtml( + sprintf(messages.gettext('No result for %(searchTerm)s.'), { searchTerm }), + )} + + {messages.gettext('Try a different search.')} + + )} + + {canEditSplitTunneling && ( + + + + )} + ); } @@ -590,10 +603,7 @@ function ApplicationList(props: IApplicationListProps ); } else { return ( - + a.name.localeCompare(b.name))} diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/SplitTunnelingSettingsStyles.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/SplitTunnelingSettingsStyles.tsx index 0d6fba2591fe..554f2a2a1140 100644 --- a/desktop/packages/mullvad-vpn/src/renderer/components/SplitTunnelingSettingsStyles.tsx +++ b/desktop/packages/mullvad-vpn/src/renderer/components/SplitTunnelingSettingsStyles.tsx @@ -85,7 +85,7 @@ export const StyledNoResult = styled(Cell.CellFooter)({ flexDirection: 'column', paddingTop: 0, marginTop: 0, - marginBottom: Spacings.spacing12, + marginBottom: Spacings.spacing9, }); export const StyledNoResultText = styled(Cell.CellFooterText)({ diff --git a/desktop/packages/mullvad-vpn/test/e2e/installed/state-dependent/custom-bridge.spec.ts b/desktop/packages/mullvad-vpn/test/e2e/installed/state-dependent/custom-bridge.spec.ts index 34efb60f268b..c022329e7495 100644 --- a/desktop/packages/mullvad-vpn/test/e2e/installed/state-dependent/custom-bridge.spec.ts +++ b/desktop/packages/mullvad-vpn/test/e2e/installed/state-dependent/custom-bridge.spec.ts @@ -36,10 +36,9 @@ test('App should enable bridge mode', async () => { RoutePath.openVpnSettings, ); - await page.getByTestId('bridge-mode-on').click(); - await expect(page.getByText('Enable bridge mode?')).toBeVisible(); - - await page.getByTestId('enable-confirm').click(); + const bridgeModeOnButton = page.getByTestId('bridge-mode-on'); + await bridgeModeOnButton.click(); + await expect(bridgeModeOnButton).toHaveAttribute('aria-selected', 'true'); await util.waitForNavigation(() => page.click('button[aria-label="Back"]')); await util.waitForNavigation(() => page.click('button[aria-label="Back"]')); diff --git a/test/test-manager/src/tests/install.rs b/test/test-manager/src/tests/install.rs index ee4062ae8981..936f07d65d79 100644 --- a/test/test-manager/src/tests/install.rs +++ b/test/test-manager/src/tests/install.rs @@ -28,16 +28,14 @@ pub async fn test_upgrade_app( _mullvad_client: MullvadClientArgument, ) -> anyhow::Result<()> { // Install the older version of the app and verify that it is running. - install_app( - &rpc, - TEST_CONFIG - .app_package_to_upgrade_from_filename - .as_ref() - .unwrap(), - &ctx.rpc_provider, - ) - .await - .context("Failed to install previous app version")?; + let old_version = TEST_CONFIG + .app_package_to_upgrade_from_filename + .as_ref() + .context("Could not find previous app version")?; + log::debug!("Installing app version {old_version}"); + install_app(&rpc, old_version, &ctx.rpc_provider) + .await + .context("Failed to install previous app version")?; // Verify that daemon is running if rpc.mullvad_daemon_get_status().await? != ServiceStatus::Running { @@ -104,9 +102,10 @@ pub async fn test_upgrade_app( tokio::time::sleep(Duration::from_secs(3)).await; // verify that daemon is running - if rpc.mullvad_daemon_get_status().await? != ServiceStatus::Running { - bail!(Error::DaemonNotRunning); - } + ensure!( + rpc.mullvad_daemon_get_status().await? == ServiceStatus::Running, + Error::DaemonNotRunning + ); // Verify that the correct version was installed let running_daemon_version = rpc.mullvad_daemon_version().await?; diff --git a/test/test-rpc/src/lib.rs b/test/test-rpc/src/lib.rs index 7c10f0df5317..a23eb8426645 100644 --- a/test/test-rpc/src/lib.rs +++ b/test/test-rpc/src/lib.rs @@ -47,8 +47,14 @@ pub enum Error { Ping(String), #[error("Failed to get or set registry value: {0}")] Registry(String), + #[error("Failed to start the service: {0}")] + ServiceStart(String), + #[error("Failed to stop the service: {0}")] + ServiceStop(String), #[error("Failed to change the service: {0}")] - Service(String), + ServiceChange(String), + #[error("Failed to find the service: {0}")] + ServiceNotFound(String), #[error("Could not read from or write to the file system: {0}")] FileSystem(String), #[error("Could not serialize or deserialize file: {0}")] diff --git a/test/test-runner/src/app.rs b/test/test-runner/src/app.rs index 6c6ed0b369be..009638c31515 100644 --- a/test/test-runner/src/app.rs +++ b/test/test-runner/src/app.rs @@ -5,11 +5,18 @@ use test_rpc::{AppTrace, Error}; /// Get the installed app version string pub async fn version() -> Result { - let version = tokio::process::Command::new("mullvad") + // The `mullvad` binary is seemingly not in PATH on Windows after upgrading the app.. + // So, as a workaround we use the absolute path instead. + const MULLVAD_CLI_BIN: &str = if cfg!(target_os = "windows") { + r"C:\Program Files\Mullvad VPN\resources\mullvad.exe" + } else { + "mullvad" + }; + let version = tokio::process::Command::new(MULLVAD_CLI_BIN) .arg("--version") .output() .await - .map_err(|e| Error::Service(e.to_string()))?; + .map_err(|e| Error::ServiceNotFound(e.to_string()))?; let version = String::from_utf8(version.stdout).map_err(|err| Error::Other(err.to_string()))?; // HACK: The output from `mullvad --version` includes the `mullvad-cli` binary name followed by // the version string. Simply remove the leading noise and get at the version string. diff --git a/test/test-runner/src/sys.rs b/test/test-runner/src/sys.rs index e1060124d7f4..56edcc75d321 100644 --- a/test/test-runner/src/sys.rs +++ b/test/test-runner/src/sys.rs @@ -180,7 +180,7 @@ ExecStart=/usr/bin/mullvad-daemon --disable-stdout-timestamps {verbosity}"# if let Some(parent) = override_path.parent() { tokio::fs::create_dir_all(parent) .await - .map_err(|e| test_rpc::Error::Service(e.to_string()))?; + .map_err(|e| test_rpc::Error::ServiceChange(e.to_string()))?; } let mut file = tokio::fs::OpenOptions::new() @@ -189,17 +189,17 @@ ExecStart=/usr/bin/mullvad-daemon --disable-stdout-timestamps {verbosity}"# .write(true) .open(override_path) .await - .map_err(|e| test_rpc::Error::Service(e.to_string()))?; + .map_err(|e| test_rpc::Error::ServiceChange(e.to_string()))?; file.write_all(systemd_service_file_content.as_bytes()) .await - .map_err(|e| test_rpc::Error::Service(e.to_string()))?; + .map_err(|e| test_rpc::Error::ServiceChange(e.to_string()))?; tokio::process::Command::new("systemctl") .args(["daemon-reload"]) .status() .await - .map_err(|e| test_rpc::Error::Service(e.to_string()))?; + .map_err(|e| test_rpc::Error::ServiceStart(e.to_string()))?; restart_app().await?; Ok(()) @@ -214,7 +214,7 @@ pub async fn restart_app() -> Result<(), test_rpc::Error> { .args(["restart", "mullvad-daemon"]) .status() .await - .map_err(|e| test_rpc::Error::Service(e.to_string()))?; + .map_err(|e| test_rpc::Error::ServiceStart(e.to_string()))?; wait_for_service_state(ServiceState::Running).await?; Ok(()) } @@ -228,7 +228,7 @@ pub async fn stop_app() -> Result<(), test_rpc::Error> { .args(["stop", "mullvad-daemon"]) .status() .await - .map_err(|e| test_rpc::Error::Service(e.to_string()))?; + .map_err(|e| test_rpc::Error::ServiceStop(e.to_string()))?; wait_for_service_state(ServiceState::Inactive).await?; Ok(()) @@ -243,7 +243,7 @@ pub async fn start_app() -> Result<(), test_rpc::Error> { .args(["start", "mullvad-daemon"]) .status() .await - .map_err(|e| test_rpc::Error::Service(e.to_string()))?; + .map_err(|e| test_rpc::Error::ServiceStart(e.to_string()))?; wait_for_service_state(ServiceState::Running).await?; Ok(()) } @@ -267,7 +267,7 @@ pub async fn stop_app() -> Result<(), test_rpc::Error> { .args(["stop", "mullvadvpn"]) .status() .await - .map_err(|e| test_rpc::Error::Service(e.to_string()))?; + .map_err(|e| test_rpc::Error::ServiceStop(e.to_string()))?; Ok(()) } @@ -280,7 +280,7 @@ pub async fn start_app() -> Result<(), test_rpc::Error> { .args(["start", "mullvadvpn"]) .status() .await - .map_err(|e| test_rpc::Error::Service(e.to_string()))?; + .map_err(|e| test_rpc::Error::ServiceStart(e.to_string()))?; Ok(()) } @@ -325,7 +325,7 @@ pub async fn set_daemon_log_level(verbosity_level: Verbosity) -> Result<(), test }; let manager = ServiceManager::local_computer(None::<&str>, ServiceManagerAccess::CONNECT) - .map_err(|e| test_rpc::Error::Service(e.to_string()))?; + .map_err(|e| test_rpc::Error::ServiceNotFound(e.to_string()))?; let service = manager .open_service( "mullvadvpn", @@ -334,23 +334,23 @@ pub async fn set_daemon_log_level(verbosity_level: Verbosity) -> Result<(), test | ServiceAccess::START | ServiceAccess::STOP, ) - .map_err(|e| test_rpc::Error::Service(e.to_string()))?; + .map_err(|e| test_rpc::Error::ServiceNotFound(e.to_string()))?; // Stop the service // TODO: Extract to separate function. service .stop() - .map_err(|e| test_rpc::Error::Service(e.to_string()))?; + .map_err(|e| test_rpc::Error::ServiceStop(e.to_string()))?; tokio::process::Command::new("net") .args(["stop", "mullvadvpn"]) .status() .await - .map_err(|e| test_rpc::Error::Service(e.to_string()))?; + .map_err(|e| test_rpc::Error::ServiceStop(e.to_string()))?; // Get the current service configuration let config = service .query_config() - .map_err(|e| test_rpc::Error::Service(e.to_string()))?; + .map_err(|e| test_rpc::Error::ServiceNotFound(e.to_string()))?; let executable_path = "C:\\Program Files\\Mullvad VPN\\resources\\mullvad-daemon.exe"; let launch_arguments = vec![ @@ -375,13 +375,13 @@ pub async fn set_daemon_log_level(verbosity_level: Verbosity) -> Result<(), test // Apply the updated configuration service .change_config(&updated_config) - .map_err(|e| test_rpc::Error::Service(e.to_string()))?; + .map_err(|e| test_rpc::Error::ServiceChange(e.to_string()))?; // Start the service // TODO: Extract to separate function. service .start::(&[]) - .map_err(|e| test_rpc::Error::Service(e.to_string()))?; + .map_err(|e| test_rpc::Error::ServiceNotFound(e.to_string()))?; Ok(()) } @@ -445,19 +445,19 @@ pub async fn set_daemon_environment(env: HashMap) -> Result<(), .map(|env_var| env_var.to_systemd_string()) { writeln!(&mut override_content, "{env_var}") - .map_err(|err| test_rpc::Error::Service(err.to_string()))?; + .map_err(|err| test_rpc::Error::ServiceChange(err.to_string()))?; } let override_path = std::path::Path::new(SYSTEMD_OVERRIDE_FILE); if let Some(parent) = override_path.parent() { tokio::fs::create_dir_all(parent) .await - .map_err(|e| test_rpc::Error::Service(e.to_string()))?; + .map_err(|e| test_rpc::Error::ServiceChange(e.to_string()))?; } tokio::fs::write(override_path, override_content) .await - .map_err(|e| test_rpc::Error::Service(e.to_string()))?; + .map_err(|e| test_rpc::Error::ServiceChange(e.to_string()))?; if tokio::process::Command::new("systemctl") .args(["daemon-reload"]) @@ -467,7 +467,7 @@ pub async fn set_daemon_environment(env: HashMap) -> Result<(), .success() .not() { - return Err(test_rpc::Error::Service( + return Err(test_rpc::Error::ServiceChange( "Daemon service could not be reloaded".to_owned(), )); }; @@ -480,7 +480,7 @@ pub async fn set_daemon_environment(env: HashMap) -> Result<(), .success() .not() { - return Err(test_rpc::Error::Service( + return Err(test_rpc::Error::ServiceStart( "Daemon service could not be restarted".to_owned(), )); }; @@ -541,8 +541,9 @@ pub fn get_system_path_var() -> Result { #[cfg(target_os = "macos")] pub async fn set_daemon_environment(env: HashMap) -> Result<(), test_rpc::Error> { tokio::task::spawn_blocking(|| { - let mut parsed_plist = plist::Value::from_file(PLIST_OVERRIDE_FILE) - .map_err(|error| test_rpc::Error::Service(format!("failed to parse plist: {error}")))?; + let mut parsed_plist = plist::Value::from_file(PLIST_OVERRIDE_FILE).map_err(|error| { + test_rpc::Error::ServiceNotFound(format!("failed to parse plist: {error}")) + })?; let mut vars = plist::Dictionary::new(); for (k, v) in env { @@ -551,25 +552,25 @@ pub async fn set_daemon_environment(env: HashMap) -> Result<(), .arg("setenv") .args([&k, &v]) .status() - .map_err(|e| test_rpc::Error::Service(e.to_string()))?; + .map_err(|e| test_rpc::Error::ServiceChange(e.to_string()))?; vars.insert(k, plist::Value::String(v)); } // Add permanent env var parsed_plist .as_dictionary_mut() - .ok_or_else(|| test_rpc::Error::Service("plist missing dict".to_owned()))? + .ok_or_else(|| test_rpc::Error::ServiceChange("plist missing dict".to_owned()))? .insert( "EnvironmentVariables".to_owned(), plist::Value::Dictionary(vars), ); let daemon_plist = std::fs::File::create(PLIST_OVERRIDE_FILE) - .map_err(|e| test_rpc::Error::Service(format!("failed to open plist: {e}")))?; + .map_err(|e| test_rpc::Error::ServiceChange(format!("failed to open plist: {e}")))?; parsed_plist .to_writer_xml(daemon_plist) - .map_err(|e| test_rpc::Error::Service(format!("failed to replace plist: {e}")))?; + .map_err(|e| test_rpc::Error::ServiceChange(format!("failed to replace plist: {e}")))?; Ok::<(), test_rpc::Error>(()) }) @@ -586,15 +587,20 @@ pub async fn set_daemon_environment(env: HashMap) -> Result<(), #[cfg(target_os = "macos")] async fn set_launch_daemon_state(on: bool) -> Result<(), test_rpc::Error> { - tokio::process::Command::new("launchctl") - .args([ - if on { "load" } else { "unload" }, - "-w", - PLIST_OVERRIDE_FILE, - ]) - .status() - .await - .map_err(|e| test_rpc::Error::Service(e.to_string()))?; + let mut launchctl = tokio::process::Command::new("launchctl"); + if on { + launchctl + .args(["load", "-w", PLIST_OVERRIDE_FILE]) + .status() + .await + .map_err(|e| test_rpc::Error::ServiceStart(e.to_string()))?; + } else { + launchctl + .args(["unload", "-w", PLIST_OVERRIDE_FILE]) + .status() + .await + .map_err(|e| test_rpc::Error::ServiceStop(e.to_string()))?; + } Ok(()) } @@ -665,8 +671,9 @@ pub async fn get_daemon_environment() -> Result, test_rp #[cfg(target_os = "macos")] pub async fn get_daemon_environment() -> Result, test_rpc::Error> { let plist = tokio::task::spawn_blocking(|| { - let parsed_plist = plist::Value::from_file(PLIST_OVERRIDE_FILE) - .map_err(|error| test_rpc::Error::Service(format!("failed to parse plist: {error}")))?; + let parsed_plist = plist::Value::from_file(PLIST_OVERRIDE_FILE).map_err(|error| { + test_rpc::Error::ServiceNotFound(format!("failed to parse plist: {error}")) + })?; Ok::(parsed_plist) }) @@ -675,14 +682,14 @@ pub async fn get_daemon_environment() -> Result, test_rp let plist_tree = plist .as_dictionary() - .ok_or_else(|| test_rpc::Error::Service("plist missing dict".to_owned()))?; + .ok_or_else(|| test_rpc::Error::ServiceNotFound("plist missing dict".to_owned()))?; let Some(env_vars) = plist_tree.get("EnvironmentVariables") else { // `EnvironmentVariables` does not exist in plist file, so there are no env variables to // parse. return Ok(HashMap::new()); }; let env_vars = env_vars.as_dictionary().ok_or_else(|| { - test_rpc::Error::Service("`EnvironmentVariables` is not a dict".to_owned()) + test_rpc::Error::ServiceNotFound("`EnvironmentVariables` is not a dict".to_owned()) })?; let env = env_vars @@ -707,7 +714,7 @@ async fn wait_for_service_state(awaited_state: ServiceState) -> Result<(), test_ loop { attempt += 1; if attempt > RETRY_ATTEMPTS { - return Err(test_rpc::Error::Service(String::from( + return Err(test_rpc::Error::ServiceStart(String::from( "Awaiting new service state timed out", ))); } @@ -716,7 +723,7 @@ async fn wait_for_service_state(awaited_state: ServiceState) -> Result<(), test_ .args(["status", "mullvad-daemon"]) .output() .await - .map_err(|e| test_rpc::Error::Service(e.to_string()))? + .map_err(|e| test_rpc::Error::ServiceNotFound(e.to_string()))? .stdout; let output = String::from_utf8_lossy(&output);