diff options
| author | Albin <albin@mullvad.net> | 2023-05-15 17:00:50 +0200 |
|---|---|---|
| committer | Albin <albin@mullvad.net> | 2023-05-15 17:00:50 +0200 |
| commit | b3821d62c16eda5141f7b4e5eba582d90966f4e2 (patch) | |
| tree | 9d128bd932df0a405081da8064bd74afbd47e96f | |
| parent | 6e36ff8295cbd91e1fbac9d6fa92d97c05f09017 (diff) | |
| parent | 26b857acd0701c741687e76f4f0b2a352e607c08 (diff) | |
| download | mullvadvpn-b3821d62c16eda5141f7b4e5eba582d90966f4e2.tar.xz mullvadvpn-b3821d62c16eda5141f7b4e5eba582d90966f4e2.zip | |
Merge remote-tracking branch 'origin/migrate-preferences-view-to-compose-droid-57'
17 files changed, 203 insertions, 243 deletions
diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/AdvancedSettingsScreenTest.kt b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/AdvancedSettingsScreenTest.kt index 340b903886..970e877b80 100644 --- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/AdvancedSettingsScreenTest.kt +++ b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/AdvancedSettingsScreenTest.kt @@ -3,10 +3,13 @@ package net.mullvad.mullvadvpn.compose.screen import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.ui.test.assertIsEnabled import androidx.compose.ui.test.assertIsNotEnabled +import androidx.compose.ui.test.hasTestTag import androidx.compose.ui.test.junit4.createComposeRule import androidx.compose.ui.test.onNodeWithContentDescription +import androidx.compose.ui.test.onNodeWithTag import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick +import androidx.compose.ui.test.performScrollToNode import androidx.compose.ui.test.performTextInput import io.mockk.MockKAnnotations import io.mockk.mockk @@ -15,6 +18,8 @@ import io.mockk.verifyAll import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.asSharedFlow import net.mullvad.mullvadvpn.compose.state.AdvancedSettingsUiState +import net.mullvad.mullvadvpn.compose.test.LAZY_LIST_LAST_ITEM_TEST_TAG +import net.mullvad.mullvadvpn.compose.test.LAZY_LIST_TEST_TAG import net.mullvad.mullvadvpn.viewmodel.CustomDnsItem import net.mullvad.mullvadvpn.viewmodel.StagedDns import org.junit.Before @@ -249,13 +254,15 @@ class AdvancedSettingsScreenTest { listOf( CustomDnsItem(address = DUMMY_DNS_ADDRESS, false), CustomDnsItem(address = DUMMY_DNS_ADDRESS_2, false), - CustomDnsItem(address = DUMMY_DNS_ADDRESS_3, false), - ), + CustomDnsItem(address = DUMMY_DNS_ADDRESS_3, false) + ) ), toastMessagesSharedFlow = MutableSharedFlow<String>().asSharedFlow() ) } - + composeTestRule + .onNodeWithTag(LAZY_LIST_TEST_TAG) + .performScrollToNode(hasTestTag(LAZY_LIST_LAST_ITEM_TEST_TAG)) // Assert composeTestRule.apply { onNodeWithText(DUMMY_DNS_ADDRESS).assertExists() @@ -274,12 +281,14 @@ class AdvancedSettingsScreenTest { uiState = AdvancedSettingsUiState.DefaultUiState( isCustomDnsEnabled = false, - customDnsItems = listOf(CustomDnsItem(address = DUMMY_DNS_ADDRESS, false)), + customDnsItems = listOf(CustomDnsItem(address = DUMMY_DNS_ADDRESS, false)) ), toastMessagesSharedFlow = MutableSharedFlow<String>().asSharedFlow() ) } - + composeTestRule + .onNodeWithTag(LAZY_LIST_TEST_TAG) + .performScrollToNode(hasTestTag(LAZY_LIST_LAST_ITEM_TEST_TAG)) // Assert composeTestRule.onNodeWithText(DUMMY_DNS_ADDRESS).assertDoesNotExist() composeTestRule.onNodeWithText("Add a server").assertDoesNotExist() @@ -296,7 +305,7 @@ class AdvancedSettingsScreenTest { isCustomDnsEnabled = true, isAllowLanEnabled = true, customDnsItems = - listOf(CustomDnsItem(address = DUMMY_DNS_ADDRESS, isLocal = true)), + listOf(CustomDnsItem(address = DUMMY_DNS_ADDRESS, isLocal = true)) ), toastMessagesSharedFlow = MutableSharedFlow<String>().asSharedFlow() ) @@ -317,7 +326,7 @@ class AdvancedSettingsScreenTest { isCustomDnsEnabled = true, isAllowLanEnabled = false, customDnsItems = - listOf(CustomDnsItem(address = DUMMY_DNS_ADDRESS, isLocal = false)), + listOf(CustomDnsItem(address = DUMMY_DNS_ADDRESS, isLocal = false)) ), toastMessagesSharedFlow = MutableSharedFlow<String>().asSharedFlow() ) @@ -338,7 +347,7 @@ class AdvancedSettingsScreenTest { isCustomDnsEnabled = true, isAllowLanEnabled = true, customDnsItems = - listOf(CustomDnsItem(address = DUMMY_DNS_ADDRESS, isLocal = false)), + listOf(CustomDnsItem(address = DUMMY_DNS_ADDRESS, isLocal = false)) ), toastMessagesSharedFlow = MutableSharedFlow<String>().asSharedFlow() ) @@ -359,7 +368,7 @@ class AdvancedSettingsScreenTest { isCustomDnsEnabled = true, isAllowLanEnabled = false, customDnsItems = - listOf(CustomDnsItem(address = DUMMY_DNS_ADDRESS, isLocal = true)), + listOf(CustomDnsItem(address = DUMMY_DNS_ADDRESS, isLocal = true)) ), toastMessagesSharedFlow = MutableSharedFlow<String>().asSharedFlow() ) @@ -383,6 +392,9 @@ class AdvancedSettingsScreenTest { toastMessagesSharedFlow = MutableSharedFlow<String>().asSharedFlow() ) } + composeTestRule + .onNodeWithTag(LAZY_LIST_TEST_TAG) + .performScrollToNode(hasTestTag(LAZY_LIST_LAST_ITEM_TEST_TAG)) // Act composeTestRule.onNodeWithText("Add a server").performClick() @@ -401,7 +413,7 @@ class AdvancedSettingsScreenTest { AdvancedSettingsUiState.DnsDialogUiState( stagedDns = StagedDns.NewDns( - item = CustomDnsItem(DUMMY_DNS_ADDRESS, isLocal = false), + item = CustomDnsItem(DUMMY_DNS_ADDRESS, isLocal = false) ), ), toastMessagesSharedFlow = MutableSharedFlow<String>().asSharedFlow() @@ -423,8 +435,8 @@ class AdvancedSettingsScreenTest { stagedDns = StagedDns.EditDns( item = CustomDnsItem(DUMMY_DNS_ADDRESS, isLocal = false), - index = 0, - ), + index = 0 + ) ), toastMessagesSharedFlow = MutableSharedFlow<String>().asSharedFlow() ) @@ -445,9 +457,9 @@ class AdvancedSettingsScreenTest { stagedDns = StagedDns.NewDns( item = CustomDnsItem(DUMMY_DNS_ADDRESS, isLocal = true), - validationResult = StagedDns.ValidationResult.Success, + validationResult = StagedDns.ValidationResult.Success ), - isAllowLanEnabled = false, + isAllowLanEnabled = false ), toastMessagesSharedFlow = MutableSharedFlow<String>().asSharedFlow() ) @@ -468,9 +480,9 @@ class AdvancedSettingsScreenTest { stagedDns = StagedDns.NewDns( item = CustomDnsItem(DUMMY_DNS_ADDRESS, isLocal = true), - validationResult = StagedDns.ValidationResult.Success, + validationResult = StagedDns.ValidationResult.Success ), - isAllowLanEnabled = true, + isAllowLanEnabled = true ), toastMessagesSharedFlow = MutableSharedFlow<String>().asSharedFlow() ) @@ -491,9 +503,9 @@ class AdvancedSettingsScreenTest { stagedDns = StagedDns.NewDns( item = CustomDnsItem(DUMMY_DNS_ADDRESS, isLocal = false), - validationResult = StagedDns.ValidationResult.Success, + validationResult = StagedDns.ValidationResult.Success ), - isAllowLanEnabled = true, + isAllowLanEnabled = true ), toastMessagesSharedFlow = MutableSharedFlow<String>().asSharedFlow() ) @@ -514,9 +526,9 @@ class AdvancedSettingsScreenTest { stagedDns = StagedDns.NewDns( item = CustomDnsItem(DUMMY_DNS_ADDRESS, isLocal = false), - validationResult = StagedDns.ValidationResult.Success, + validationResult = StagedDns.ValidationResult.Success ), - isAllowLanEnabled = false, + isAllowLanEnabled = false ), toastMessagesSharedFlow = MutableSharedFlow<String>().asSharedFlow() ) @@ -537,8 +549,8 @@ class AdvancedSettingsScreenTest { stagedDns = StagedDns.NewDns( item = CustomDnsItem(DUMMY_DNS_ADDRESS, isLocal = false), - validationResult = StagedDns.ValidationResult.InvalidAddress, - ), + validationResult = StagedDns.ValidationResult.InvalidAddress + ) ), toastMessagesSharedFlow = MutableSharedFlow<String>().asSharedFlow() ) @@ -559,8 +571,8 @@ class AdvancedSettingsScreenTest { stagedDns = StagedDns.NewDns( item = CustomDnsItem(DUMMY_DNS_ADDRESS, isLocal = false), - validationResult = StagedDns.ValidationResult.DuplicateAddress, - ), + validationResult = StagedDns.ValidationResult.DuplicateAddress + ) ), toastMessagesSharedFlow = MutableSharedFlow<String>().asSharedFlow() ) diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/SwitchComposeCell.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/SwitchComposeCell.kt index 980af59693..0b5508d524 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/SwitchComposeCell.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/SwitchComposeCell.kt @@ -46,13 +46,14 @@ private fun PreviewSwitchComposeCell() { fun SwitchComposeCell( title: String, isToggled: Boolean, + subtitle: String? = null, isEnabled: Boolean = true, background: Color = MullvadBlue, onCellClicked: (Boolean) -> Unit = {}, onInfoClicked: (() -> Unit)? = null ) { - val subtitleModifier = Modifier + val textSize = dimensionResource(id = R.dimen.text_small).value.sp BaseCell( title = { SwitchCellTitle( @@ -60,6 +61,10 @@ fun SwitchComposeCell( modifier = Modifier.alpha(if (isEnabled) AlphaActive else AlphaInactive) ) }, + subtitle = + subtitle?.let { + @Composable { Text(text = it, fontSize = textSize, color = MullvadWhite60) } + }, isRowEnabled = isEnabled, bodyView = { SwitchCellView( @@ -70,8 +75,7 @@ fun SwitchComposeCell( ) }, background = background, - onCellClicked = { onCellClicked(!isToggled) }, - subtitleModifier = subtitleModifier + onCellClicked = { onCellClicked(!isToggled) } ) } @@ -100,7 +104,7 @@ fun SwitchCellView( val verticalPadding = 13.dp Row( modifier = modifier.wrapContentWidth().wrapContentHeight(), - verticalAlignment = Alignment.CenterVertically, + verticalAlignment = Alignment.CenterVertically ) { if (onInfoClicked != null) { Icon( @@ -110,7 +114,7 @@ fun SwitchCellView( start = horizontalPadding, end = horizontalPadding, top = verticalPadding, - bottom = verticalPadding, + bottom = verticalPadding ) .align(Alignment.CenterVertically), painter = painterResource(id = R.drawable.icon_info), diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/LocalNetworkSharingInfoDialog.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/LocalNetworkSharingInfoDialog.kt new file mode 100644 index 0000000000..89ef3cb3a6 --- /dev/null +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/LocalNetworkSharingInfoDialog.kt @@ -0,0 +1,19 @@ +package net.mullvad.mullvadvpn.compose.dialog + +import androidx.compose.runtime.Composable +import androidx.compose.ui.res.stringResource +import net.mullvad.mullvadvpn.R +import net.mullvad.mullvadvpn.compose.component.textResource + +@Composable +fun LocalNetworkSharingInfoDialog(onDismiss: () -> Unit) { + InfoDialog( + message = stringResource(id = R.string.local_network_sharing_info), + additionalInfo = + buildString { + appendLine(stringResource(id = R.string.local_network_sharing_additional_info)) + appendLine(textResource(id = R.string.local_network_sharing_ip_ranges)) + }, + onDismiss = onDismiss + ) +} diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/AdvancedSettingScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/AdvancedSettingScreen.kt index c07d188e42..56aedb69ca 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/AdvancedSettingScreen.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/AdvancedSettingScreen.kt @@ -28,6 +28,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalLifecycleOwner +import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview @@ -58,11 +59,14 @@ import net.mullvad.mullvadvpn.compose.component.drawVerticalScrollbar import net.mullvad.mullvadvpn.compose.dialog.ContentBlockersInfoDialog import net.mullvad.mullvadvpn.compose.dialog.CustomDnsInfoDialog import net.mullvad.mullvadvpn.compose.dialog.DnsDialog +import net.mullvad.mullvadvpn.compose.dialog.LocalNetworkSharingInfoDialog import net.mullvad.mullvadvpn.compose.dialog.MalwareInfoDialog import net.mullvad.mullvadvpn.compose.dialog.MtuDialog import net.mullvad.mullvadvpn.compose.dialog.ObfuscationInfoDialog import net.mullvad.mullvadvpn.compose.extensions.itemWithDivider import net.mullvad.mullvadvpn.compose.state.AdvancedSettingsUiState +import net.mullvad.mullvadvpn.compose.test.LAZY_LIST_LAST_ITEM_TEST_TAG +import net.mullvad.mullvadvpn.compose.test.LAZY_LIST_TEST_TAG import net.mullvad.mullvadvpn.compose.theme.MullvadBlue20 import net.mullvad.mullvadvpn.compose.theme.MullvadDarkBlue import net.mullvad.mullvadvpn.compose.theme.MullvadGreen @@ -76,6 +80,7 @@ private fun PreviewAdvancedSettings() { AdvancedSettingScreen( uiState = AdvancedSettingsUiState.DefaultUiState( + isAutoConnectEnabled = true, mtu = "1337", isCustomDnsEnabled = true, customDnsItems = listOf(CustomDnsItem("0.0.0.0", false)), @@ -86,6 +91,8 @@ private fun PreviewAdvancedSettings() { onRestoreMtuClick = {}, onCancelMtuDialogClicked = {}, onSplitTunnelingNavigationClick = {}, + onToggleAutoConnect = {}, + onToggleLocalNetworkSharing = {}, onToggleDnsClick = {}, onToggleBlockAds = {}, onToggleBlockTrackers = {}, @@ -97,6 +104,7 @@ private fun PreviewAdvancedSettings() { onSaveDnsClick = {}, onRemoveDnsClick = {}, onCancelDnsDialogClick = {}, + onLocalNetworkSharingInfoClick = {}, onContentsBlockersInfoClicked = {}, onMalwareInfoClicked = {}, onCustomDnsInfoClicked = {}, @@ -121,6 +129,8 @@ fun AdvancedSettingScreen( onRestoreMtuClick: () -> Unit = {}, onCancelMtuDialogClicked: () -> Unit = {}, onSplitTunnelingNavigationClick: () -> Unit = {}, + onToggleAutoConnect: (Boolean) -> Unit = {}, + onToggleLocalNetworkSharing: (Boolean) -> Unit = {}, onToggleDnsClick: (Boolean) -> Unit = {}, onToggleBlockAds: (Boolean) -> Unit = {}, onToggleBlockTrackers: (Boolean) -> Unit = {}, @@ -132,6 +142,7 @@ fun AdvancedSettingScreen( onSaveDnsClick: () -> Unit = {}, onRemoveDnsClick: () -> Unit = {}, onCancelDnsDialogClick: () -> Unit = {}, + onLocalNetworkSharingInfoClick: () -> Unit = {}, onContentsBlockersInfoClicked: () -> Unit = {}, onMalwareInfoClicked: () -> Unit = {}, onCustomDnsInfoClicked: () -> Unit = {}, @@ -165,6 +176,9 @@ fun AdvancedSettingScreen( onDismiss = { onCancelDnsDialogClick() } ) } + is AdvancedSettingsUiState.LocalNetworkSharingInfoDialogUiState -> { + LocalNetworkSharingInfoDialog(onDismissInfoClicked) + } is AdvancedSettingsUiState.ContentBlockersInfoDialogUiState -> { ContentBlockersInfoDialog(onDismissInfoClicked) } @@ -229,12 +243,36 @@ fun AdvancedSettingScreen( LazyColumn( modifier = Modifier.drawVerticalScrollbar(lazyListState) + .testTag(LAZY_LIST_TEST_TAG) .fillMaxWidth() .wrapContentHeight() .animateContentSize(), state = lazyListState ) { - item { MtuComposeCell(mtuValue = uiState.mtu, onEditMtu = { onMtuCellClick() }) } + item { + Spacer(modifier = Modifier.height(cellVerticalSpacing)) + SwitchComposeCell( + title = stringResource(R.string.auto_connect), + subtitle = stringResource(id = R.string.auto_connect_footer), + isToggled = uiState.isAutoConnectEnabled, + isEnabled = true, + onCellClicked = { newValue -> onToggleAutoConnect(newValue) } + ) + } + item { + Spacer(modifier = Modifier.height(cellVerticalSpacing)) + SwitchComposeCell( + title = stringResource(R.string.local_network_sharing), + isToggled = uiState.isAllowLanEnabled, + isEnabled = true, + onCellClicked = { newValue -> onToggleLocalNetworkSharing(newValue) }, + onInfoClicked = { onLocalNetworkSharingInfoClick() } + ) + } + item { + Spacer(modifier = Modifier.height(cellVerticalSpacing)) + MtuComposeCell(mtuValue = uiState.mtu, onEditMtu = { onMtuCellClick() }) + } itemWithDivider { NavigationComposeCell( @@ -417,6 +455,7 @@ fun AdvancedSettingScreen( isCellClickable = uiState.contentBlockersOptions.isAnyBlockerEnabled().not(), modifier = Modifier.background(MullvadDarkBlue) + .testTag(LAZY_LIST_LAST_ITEM_TEST_TAG) .padding( start = cellHorizontalSpacing, top = topPadding, diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/AdvancedSettingsUiState.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/AdvancedSettingsUiState.kt index f08a654078..261714f8f9 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/AdvancedSettingsUiState.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/AdvancedSettingsUiState.kt @@ -7,6 +7,8 @@ import net.mullvad.mullvadvpn.viewmodel.StagedDns sealed interface AdvancedSettingsUiState { val mtu: String + val isAutoConnectEnabled: Boolean + val isLocalNetworkSharingEnabled: Boolean val isCustomDnsEnabled: Boolean val customDnsItems: List<CustomDnsItem> val contentBlockersOptions: DefaultDnsOptions @@ -15,66 +17,91 @@ sealed interface AdvancedSettingsUiState { data class DefaultUiState( override val mtu: String = "", + override val isAutoConnectEnabled: Boolean = false, + override val isLocalNetworkSharingEnabled: Boolean = false, override val isCustomDnsEnabled: Boolean = false, override val isAllowLanEnabled: Boolean = false, override val customDnsItems: List<CustomDnsItem> = listOf(), override val contentBlockersOptions: DefaultDnsOptions = DefaultDnsOptions(), - override val selectedObfuscation: SelectedObfuscation = SelectedObfuscation.Off, + override val selectedObfuscation: SelectedObfuscation = SelectedObfuscation.Off ) : AdvancedSettingsUiState data class MtuDialogUiState( override val mtu: String = "", + override val isAutoConnectEnabled: Boolean = false, + override val isLocalNetworkSharingEnabled: Boolean = false, override val isCustomDnsEnabled: Boolean = false, override val isAllowLanEnabled: Boolean = false, override val customDnsItems: List<CustomDnsItem> = listOf(), override val contentBlockersOptions: DefaultDnsOptions = DefaultDnsOptions(), val mtuEditValue: String, - override val selectedObfuscation: SelectedObfuscation = SelectedObfuscation.Off, + override val selectedObfuscation: SelectedObfuscation = SelectedObfuscation.Off ) : AdvancedSettingsUiState data class DnsDialogUiState( override val mtu: String = "", + override val isAutoConnectEnabled: Boolean = false, + override val isLocalNetworkSharingEnabled: Boolean = false, override val isCustomDnsEnabled: Boolean = false, override val isAllowLanEnabled: Boolean = false, override val customDnsItems: List<CustomDnsItem> = listOf(), override val contentBlockersOptions: DefaultDnsOptions = DefaultDnsOptions(), val stagedDns: StagedDns, - override val selectedObfuscation: SelectedObfuscation = SelectedObfuscation.Off, + override val selectedObfuscation: SelectedObfuscation = SelectedObfuscation.Off + ) : AdvancedSettingsUiState + + data class LocalNetworkSharingInfoDialogUiState( + override val mtu: String = "", + override val isAutoConnectEnabled: Boolean = false, + override val isLocalNetworkSharingEnabled: Boolean = false, + override val isCustomDnsEnabled: Boolean = false, + override val isAllowLanEnabled: Boolean = false, + override val customDnsItems: List<CustomDnsItem> = listOf(), + override val contentBlockersOptions: DefaultDnsOptions = DefaultDnsOptions(), + override val selectedObfuscation: SelectedObfuscation = SelectedObfuscation.Off ) : AdvancedSettingsUiState data class ContentBlockersInfoDialogUiState( override val mtu: String = "", + override val isAutoConnectEnabled: Boolean = false, + override val isLocalNetworkSharingEnabled: Boolean = false, override val isCustomDnsEnabled: Boolean = false, override val isAllowLanEnabled: Boolean = false, override val customDnsItems: List<CustomDnsItem> = listOf(), override val contentBlockersOptions: DefaultDnsOptions = DefaultDnsOptions(), - override val selectedObfuscation: SelectedObfuscation = SelectedObfuscation.Off, + override val selectedObfuscation: SelectedObfuscation = SelectedObfuscation.Off ) : AdvancedSettingsUiState data class CustomDnsInfoDialogUiState( override val mtu: String = "", + override val isAutoConnectEnabled: Boolean = false, + override val isLocalNetworkSharingEnabled: Boolean = false, override val isCustomDnsEnabled: Boolean = false, override val isAllowLanEnabled: Boolean = false, override val customDnsItems: List<CustomDnsItem> = listOf(), override val contentBlockersOptions: DefaultDnsOptions = DefaultDnsOptions(), - override val selectedObfuscation: SelectedObfuscation = SelectedObfuscation.Off, + override val selectedObfuscation: SelectedObfuscation = SelectedObfuscation.Off ) : AdvancedSettingsUiState data class MalwareInfoDialogUiState( override val mtu: String = "", + override val isAutoConnectEnabled: Boolean = false, + override val isLocalNetworkSharingEnabled: Boolean = false, override val isCustomDnsEnabled: Boolean = false, override val isAllowLanEnabled: Boolean = false, override val customDnsItems: List<CustomDnsItem> = listOf(), override val contentBlockersOptions: DefaultDnsOptions = DefaultDnsOptions(), - override val selectedObfuscation: SelectedObfuscation = SelectedObfuscation.Off, + override val selectedObfuscation: SelectedObfuscation = SelectedObfuscation.Off ) : AdvancedSettingsUiState data class ObfuscationInfoDialogUiState( override val mtu: String = "", + override val isAutoConnectEnabled: Boolean = false, + override val isLocalNetworkSharingEnabled: Boolean = false, override val isCustomDnsEnabled: Boolean = false, override val isAllowLanEnabled: Boolean = false, override val customDnsItems: List<CustomDnsItem> = listOf(), override val contentBlockersOptions: DefaultDnsOptions = DefaultDnsOptions(), - override val selectedObfuscation: SelectedObfuscation = SelectedObfuscation.Off, + override val selectedObfuscation: SelectedObfuscation = SelectedObfuscation.Off ) : AdvancedSettingsUiState } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/test/ComposeTestTagConstants.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/test/ComposeTestTagConstants.kt new file mode 100644 index 0000000000..55251eb3ea --- /dev/null +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/test/ComposeTestTagConstants.kt @@ -0,0 +1,4 @@ +package net.mullvad.mullvadvpn.compose.test + +const val LAZY_LIST_TEST_TAG = "lazy_list_test_tag" +const val LAZY_LIST_LAST_ITEM_TEST_TAG = "lazy_list_last_item_test_tag" diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/repository/SettingsRepository.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/repository/SettingsRepository.kt index 9ab6eb412f..c0b9215d37 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/repository/SettingsRepository.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/repository/SettingsRepository.kt @@ -50,10 +50,6 @@ class SettingsRepository( ) } - fun isLocalNetworkSharingEnabled(): Boolean { - return serviceConnectionManager.settingsListener()?.allowLan ?: false - } - fun setWireguardMtu(value: Int?) { serviceConnectionManager.settingsListener()?.wireguardMtu = value } @@ -61,4 +57,12 @@ class SettingsRepository( fun setObfuscationOptions(value: ObfuscationSettings) { serviceConnectionManager.settingsListener()?.obfuscationSettings = value } + + fun setAutoConnect(isEnabled: Boolean) { + serviceConnectionManager.settingsListener()?.autoConnect = isEnabled + } + + fun setLocalNetworkSharing(isEnabled: Boolean) { + serviceConnectionManager.settingsListener()?.allowLan = isEnabled + } } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/AdvancedFragment.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/AdvancedFragment.kt index 4bd7ea35b8..a7dbc8116d 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/AdvancedFragment.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/AdvancedFragment.kt @@ -34,6 +34,8 @@ class AdvancedFragment : BaseFragment() { onRestoreMtuClick = vm::onRestoreMtuClick, onCancelMtuDialogClicked = vm::onCancelDialogClick, onSplitTunnelingNavigationClick = ::openSplitTunnelingFragment, + onToggleAutoConnect = vm::onToggleAutoConnect, + onToggleLocalNetworkSharing = vm::onToggleLocalNetworkSharing, onToggleDnsClick = vm::onToggleDnsClick, onToggleBlockAds = vm::onToggleBlockAds, onToggleBlockTrackers = vm::onToggleBlockTrackers, @@ -45,6 +47,7 @@ class AdvancedFragment : BaseFragment() { onSaveDnsClick = vm::onSaveDnsClick, onRemoveDnsClick = vm::onRemoveDnsClick, onCancelDnsDialogClick = vm::onCancelDialogClick, + onLocalNetworkSharingInfoClick = vm::onLocalNetworkSharingInfoClick, onContentsBlockersInfoClicked = vm::onContentsBlockerInfoClick, onCustomDnsInfoClicked = vm::onCustomDnsInfoClick, onMalwareInfoClicked = vm::onMalwareInfoClick, diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/PreferencesFragment.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/PreferencesFragment.kt deleted file mode 100644 index 9e4631c9e3..0000000000 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/PreferencesFragment.kt +++ /dev/null @@ -1,130 +0,0 @@ -package net.mullvad.mullvadvpn.ui.fragment - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.lifecycleScope -import androidx.lifecycle.repeatOnLifecycle -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.flow.emptyFlow -import kotlinx.coroutines.flow.flatMapLatest -import kotlinx.coroutines.flow.flowOf -import kotlinx.coroutines.launch -import net.mullvad.mullvadvpn.R -import net.mullvad.mullvadvpn.model.Settings -import net.mullvad.mullvadvpn.ui.CollapsibleTitleController -import net.mullvad.mullvadvpn.ui.extension.requireMainActivity -import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnectionManager -import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnectionState -import net.mullvad.mullvadvpn.ui.serviceconnection.settingsListener -import net.mullvad.mullvadvpn.ui.widget.CellSwitch -import net.mullvad.mullvadvpn.ui.widget.ToggleCell -import net.mullvad.mullvadvpn.util.JobTracker -import net.mullvad.mullvadvpn.util.callbackFlowFromNotifier -import org.koin.android.ext.android.inject - -class PreferencesFragment : BaseFragment() { - - // Injected dependencies - private val serviceConnectionManager: ServiceConnectionManager by inject() - - private lateinit var allowLanToggle: ToggleCell - private lateinit var autoConnectToggle: ToggleCell - private lateinit var titleController: CollapsibleTitleController - - @Deprecated("Refactor code to instead rely on Lifecycle.") private val jobTracker = JobTracker() - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - lifecycleScope.launchUiSubscriptionsOnResume() - } - - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - val view = inflater.inflate(R.layout.preferences, container, false) - - view.findViewById<View>(R.id.back).setOnClickListener { - requireMainActivity().onBackPressed() - } - - allowLanToggle = - view.findViewById<ToggleCell>(R.id.allow_lan).apply { - listener = { state -> - serviceConnectionManager.settingsListener()?.allowLan = - when (state) { - CellSwitch.State.ON -> true - else -> false - } - } - } - - autoConnectToggle = - view.findViewById<ToggleCell>(R.id.auto_connect).apply { - listener = { state -> - serviceConnectionManager.settingsListener()?.autoConnect = - when (state) { - CellSwitch.State.ON -> true - else -> false - } - } - } - - titleController = CollapsibleTitleController(view) - - return view - } - - override fun onDestroyView() { - titleController.onDestroy() - super.onDestroyView() - } - - private fun CoroutineScope.launchUiSubscriptionsOnResume() = launch { - repeatOnLifecycle(Lifecycle.State.RESUMED) { launchSettingsSubscription() } - } - - private fun CoroutineScope.launchSettingsSubscription() = launch { - serviceConnectionManager.connectionState - .flatMapLatest { state -> - if (state is ServiceConnectionState.ConnectedReady) { - flowOf(state.container) - } else { - emptyFlow() - } - } - .flatMapLatest { callbackFlowFromNotifier(it.settingsListener.settingsNotifier) } - .collect { settings -> - if (settings != null) { - updateUi(settings) - } - } - } - - private fun updateUi(settings: Settings) { - jobTracker.newUiJob("updateUi") { - val allowLanState = boolToSwitchState(settings.allowLan) - val autoConnectState = boolToSwitchState(settings.autoConnect) - - if (isVisible) { - allowLanToggle.state = allowLanState - autoConnectToggle.state = autoConnectState - } else { - allowLanToggle.forcefullySetState(allowLanState) - autoConnectToggle.forcefullySetState(autoConnectState) - } - } - } - - private fun boolToSwitchState(pref: Boolean): CellSwitch.State { - if (pref) { - return CellSwitch.State.ON - } else { - return CellSwitch.State.OFF - } - } -} diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/SettingsFragment.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/SettingsFragment.kt index 777f014aa0..ff3a8edcf0 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/SettingsFragment.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/SettingsFragment.kt @@ -47,7 +47,6 @@ class SettingsFragment : BaseFragment(), StatusBarPainter, NavigationBarPainter private lateinit var accountMenu: AccountCell private lateinit var appVersionMenu: AppVersionCell - private lateinit var preferencesMenu: View private lateinit var advancedMenu: View private lateinit var titleController: CollapsibleTitleController @@ -72,11 +71,6 @@ class SettingsFragment : BaseFragment(), StatusBarPainter, NavigationBarPainter targetFragment = AccountFragment::class } - preferencesMenu = - view.findViewById<NavigateCell>(R.id.preferences).apply { - targetFragment = PreferencesFragment::class - } - advancedMenu = view.findViewById<NavigateCell>(R.id.advanced).apply { targetFragment = AdvancedFragment::class @@ -185,7 +179,6 @@ class SettingsFragment : BaseFragment(), StatusBarPainter, NavigationBarPainter } accountMenu.visibility = visibility - preferencesMenu.visibility = visibility advancedMenu.visibility = visibility } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/AdvancedSettingsViewModel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/AdvancedSettingsViewModel.kt index 1d353f5457..6990b7036e 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/AdvancedSettingsViewModel.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/AdvancedSettingsViewModel.kt @@ -46,6 +46,8 @@ class AdvancedSettingsViewModel( combine(repository.settingsUpdates, dialogState) { settings, dialogState -> AdvancedSettingsViewModelState( mtuValue = settings?.mtuString() ?: "", + isAutoConnectEnabled = settings?.autoConnect ?: false, + isLocalNetworkSharingEnabled = settings?.allowLan ?: false, isCustomDnsEnabled = settings?.isCustomDnsEnabled() ?: false, customDnsList = settings?.addresses()?.asStringAddressList() ?: listOf(), contentBlockersOptions = settings?.contentBlockersSettings() @@ -100,6 +102,10 @@ class AdvancedSettingsViewModel( hideDialog() } + fun onLocalNetworkSharingInfoClick() { + dialogState.update { AdvancedSettingsDialogState.LocalNetworkSharingInfoDialog } + } + fun onContentsBlockerInfoClick() { dialogState.update { AdvancedSettingsDialogState.ContentBlockersInfoDialog } } @@ -210,6 +216,14 @@ class AdvancedSettingsViewModel( hideDialog() } + fun onToggleAutoConnect(isEnabled: Boolean) { + viewModelScope.launch(dispatcher) { repository.setAutoConnect(isEnabled) } + } + + fun onToggleLocalNetworkSharing(isEnabled: Boolean) { + viewModelScope.launch(dispatcher) { repository.setLocalNetworkSharing(isEnabled) } + } + fun onToggleDnsClick(isEnabled: Boolean) { updateCustomDnsState(isEnabled) showApplySettingChangesWarningToast() diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/AdvancedSettingsViewModelState.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/AdvancedSettingsViewModelState.kt index 3349e58b99..640f4f64ac 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/AdvancedSettingsViewModelState.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/AdvancedSettingsViewModelState.kt @@ -6,6 +6,8 @@ import net.mullvad.mullvadvpn.model.SelectedObfuscation data class AdvancedSettingsViewModelState( val mtuValue: String, + val isAutoConnectEnabled: Boolean, + val isLocalNetworkSharingEnabled: Boolean, val isCustomDnsEnabled: Boolean, val isAllowLanEnabled: Boolean, val customDnsList: List<CustomDnsItem>, @@ -18,6 +20,8 @@ data class AdvancedSettingsViewModelState( is AdvancedSettingsDialogState.MtuDialog -> AdvancedSettingsUiState.MtuDialogUiState( mtu = mtuValue, + isAutoConnectEnabled = isAutoConnectEnabled, + isLocalNetworkSharingEnabled = isLocalNetworkSharingEnabled, isCustomDnsEnabled = isCustomDnsEnabled, isAllowLanEnabled = isAllowLanEnabled, customDnsItems = customDnsList, @@ -28,6 +32,8 @@ data class AdvancedSettingsViewModelState( is AdvancedSettingsDialogState.DnsDialog -> AdvancedSettingsUiState.DnsDialogUiState( mtu = mtuValue, + isAutoConnectEnabled = isAutoConnectEnabled, + isLocalNetworkSharingEnabled = isLocalNetworkSharingEnabled, isCustomDnsEnabled = isCustomDnsEnabled, isAllowLanEnabled = isAllowLanEnabled, customDnsItems = customDnsList, @@ -35,9 +41,21 @@ data class AdvancedSettingsViewModelState( stagedDns = dialogState.stagedDns, selectedObfuscation = selectedObfuscation ) + is AdvancedSettingsDialogState.LocalNetworkSharingInfoDialog -> + AdvancedSettingsUiState.LocalNetworkSharingInfoDialogUiState( + mtu = mtuValue, + isAutoConnectEnabled = isAutoConnectEnabled, + isLocalNetworkSharingEnabled = isLocalNetworkSharingEnabled, + isCustomDnsEnabled = isCustomDnsEnabled, + isAllowLanEnabled = isAllowLanEnabled, + customDnsItems = customDnsList, + contentBlockersOptions = contentBlockersOptions + ) is AdvancedSettingsDialogState.ContentBlockersInfoDialog -> AdvancedSettingsUiState.ContentBlockersInfoDialogUiState( mtu = mtuValue, + isAutoConnectEnabled = isAutoConnectEnabled, + isLocalNetworkSharingEnabled = isLocalNetworkSharingEnabled, isCustomDnsEnabled = isCustomDnsEnabled, isAllowLanEnabled = isAllowLanEnabled, customDnsItems = customDnsList, @@ -47,6 +65,8 @@ data class AdvancedSettingsViewModelState( is AdvancedSettingsDialogState.CustomDnsInfoDialog -> AdvancedSettingsUiState.CustomDnsInfoDialogUiState( mtu = mtuValue, + isAutoConnectEnabled = isAutoConnectEnabled, + isLocalNetworkSharingEnabled = isLocalNetworkSharingEnabled, isCustomDnsEnabled = isCustomDnsEnabled, isAllowLanEnabled = isAllowLanEnabled, customDnsItems = customDnsList, @@ -55,6 +75,8 @@ data class AdvancedSettingsViewModelState( is AdvancedSettingsDialogState.MalwareInfoDialog -> AdvancedSettingsUiState.MalwareInfoDialogUiState( mtu = mtuValue, + isAutoConnectEnabled = isAutoConnectEnabled, + isLocalNetworkSharingEnabled = isLocalNetworkSharingEnabled, isCustomDnsEnabled = isCustomDnsEnabled, isAllowLanEnabled = isAllowLanEnabled, customDnsItems = customDnsList, @@ -73,6 +95,8 @@ data class AdvancedSettingsViewModelState( else -> AdvancedSettingsUiState.DefaultUiState( mtu = mtuValue, + isAutoConnectEnabled = isAutoConnectEnabled, + isLocalNetworkSharingEnabled = isLocalNetworkSharingEnabled, isCustomDnsEnabled = isCustomDnsEnabled, isAllowLanEnabled = isAllowLanEnabled, customDnsItems = customDnsList, @@ -88,6 +112,8 @@ data class AdvancedSettingsViewModelState( fun default() = AdvancedSettingsViewModelState( mtuValue = EMPTY_STRING, + isAutoConnectEnabled = false, + isLocalNetworkSharingEnabled = false, isCustomDnsEnabled = false, customDnsList = listOf(), contentBlockersOptions = DefaultDnsOptions(), @@ -105,6 +131,8 @@ sealed class AdvancedSettingsDialogState { data class DnsDialog(val stagedDns: StagedDns) : AdvancedSettingsDialogState() + object LocalNetworkSharingInfoDialog : AdvancedSettingsDialogState() + object ContentBlockersInfoDialog : AdvancedSettingsDialogState() object CustomDnsInfoDialog : AdvancedSettingsDialogState() diff --git a/android/app/src/main/res/layout/preferences.xml b/android/app/src/main/res/layout/preferences.xml deleted file mode 100644 index 70489f4429..0000000000 --- a/android/app/src/main/res/layout/preferences.xml +++ /dev/null @@ -1,60 +0,0 @@ -<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:mullvad="http://schemas.android.com/apk/res-auto" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:background="@color/darkBlue" - android:gravity="left"> - <TextView android:id="@+id/title" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/settings_preferences" - style="@style/SettingsCollapsedHeader" /> - <LinearLayout android:layout_width="match_parent" - android:layout_height="match_parent" - android:orientation="vertical"> - <FrameLayout android:layout_width="match_parent" - android:layout_height="wrap_content"> - <net.mullvad.mullvadvpn.ui.widget.BackButton android:id="@+id/back" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - mullvad:text="@string/settings" /> - <TextView android:id="@+id/collapsed_title" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginHorizontal="4dp" - android:layout_gravity="center" - android:text="@string/settings_preferences" - style="@style/SettingsCollapsedHeader" /> - </FrameLayout> - <net.mullvad.mullvadvpn.ui.widget.ListenableScrollView android:id="@+id/scroll_area" - android:layout_width="match_parent" - android:layout_height="match_parent"> - - <LinearLayout android:layout_width="match_parent" - android:layout_height="match_parent" - android:paddingBottom="@dimen/screen_vertical_margin" - android:orientation="vertical"> - <TextView android:id="@+id/expanded_title" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginTop="2dp" - android:layout_marginLeft="@dimen/side_margin" - android:lines="1" - android:text="@string/settings_preferences" - style="@style/SettingsExpandedHeader" /> - <net.mullvad.mullvadvpn.ui.widget.ToggleCell android:id="@+id/auto_connect" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginTop="@dimen/vertical_space" - mullvad:text="@string/auto_connect" - mullvad:footer="@string/auto_connect_footer" /> - <net.mullvad.mullvadvpn.ui.widget.ToggleCell android:id="@+id/allow_lan" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginTop="@dimen/vertical_space" - mullvad:text="@string/local_network_sharing" - mullvad:footer="@string/allow_lan_footer" /> - </LinearLayout> - </net.mullvad.mullvadvpn.ui.widget.ListenableScrollView> - </LinearLayout> -</FrameLayout> diff --git a/android/app/src/main/res/layout/settings.xml b/android/app/src/main/res/layout/settings.xml index 8a00334fff..09426d0bca 100644 --- a/android/app/src/main/res/layout/settings.xml +++ b/android/app/src/main/res/layout/settings.xml @@ -48,11 +48,6 @@ android:layout_height="wrap_content" android:layout_marginTop="@dimen/vertical_space" mullvad:text="@string/settings_account" /> - <net.mullvad.mullvadvpn.ui.widget.NavigateCell android:id="@+id/preferences" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginTop="1dp" - mullvad:text="@string/settings_preferences" /> <net.mullvad.mullvadvpn.ui.widget.NavigateCell android:id="@+id/advanced" android:layout_width="match_parent" android:layout_height="wrap_content" diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml index 871ef46360..9baef011b7 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml @@ -194,4 +194,6 @@ <string name="off">Off</string> <string name="udp_over_tcp_port_info">Which TCP port the UDP-over-TCP obfuscation protocol should connect to on the VPN server.</string> <string name="created_x">Created: %s</string> + <string name="local_network_sharing_info">This feature allows access to other devices on the local network, such as for sharing, printing, streaming, etc.</string> + <string name="local_network_sharing_additional_info">It does this by allowing network communication outside the tunnel to local multicast and broadcast ranges as well as to and from these private IP ranges:</string> </resources> diff --git a/android/app/src/main/res/values/strings_non_translatable.xml b/android/app/src/main/res/values/strings_non_translatable.xml index c9c92fe66d..7c8b868059 100644 --- a/android/app/src/main/res/values/strings_non_translatable.xml +++ b/android/app/src/main/res/values/strings_non_translatable.xml @@ -9,4 +9,7 @@ <string name="privacy_policy_url" translatable="false">https://mullvad.net/help/privacy-policy/</string> <string name="split_tunneling" translatable="false">Split tunneling</string> <string name="wireguard" translatable="false">WireGuard</string> + <string name="local_network_sharing_ip_ranges"> + <![CDATA[<ul><li>10.0.0.0/8</li><li>172.16.0.0/12</li><li>192.168.0.0/16</li><li>169.254.0.0/16</li><li>0xfe80::/10</li><li>0xfc00::/7</li></ul>]]> + </string> </resources> diff --git a/android/config/lint.xml b/android/config/lint.xml index 7b2c5560dc..1d783264bf 100644 --- a/android/config/lint.xml +++ b/android/config/lint.xml @@ -4,4 +4,7 @@ <issue id="ExtraTranslation" severity="ignore" /> <issue id="MissingTranslation" severity="ignore" /> <issue id="Typos" severity="ignore" /> + <issue id="UnusedResources"> + <ignore path="res/values/strings.xml" /> + </issue> </lint> |
