Skip to content

Commit

Permalink
Add Server IP Overrides feature
Browse files Browse the repository at this point in the history
  • Loading branch information
Rawa committed Mar 18, 2024
1 parent 113d3af commit ea08149
Show file tree
Hide file tree
Showing 37 changed files with 1,290 additions and 15 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ Line wrap the file at 100 chars. Th
- Add auto connect and lockdown mode guide on platforms that has system vpn settings.
- Add 3D map to Connect screen.
- Add the ability to create and manage custom lists of relays.
- Add Server IP overrides feature.

### Changed
- Change default obfuscation setting to `auto`.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package net.mullvad.mullvadvpn.compose.button

import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import net.mullvad.mullvadvpn.R

@Composable
fun InfoIconButton(onClick: () -> Unit, modifier: Modifier = Modifier) {
IconButton(modifier = modifier, onClick = onClick) {
Icon(
painter = painterResource(id = R.drawable.icon_info),
contentDescription = null,
tint = MaterialTheme.colorScheme.onPrimary
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package net.mullvad.mullvadvpn.compose.cell

import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.IntrinsicSize
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import net.mullvad.mullvadvpn.R
import net.mullvad.mullvadvpn.lib.theme.AppTheme
import net.mullvad.mullvadvpn.lib.theme.Dimens
import net.mullvad.mullvadvpn.lib.theme.color.AlphaInactive
import net.mullvad.mullvadvpn.lib.theme.color.AlphaVisible
import net.mullvad.mullvadvpn.lib.theme.color.selected

@Preview
@Composable
private fun PreviewServerIpOverridesCell() {
AppTheme { ServerIpOverridesCell(active = true) }
}

@Composable
fun ServerIpOverridesCell(
active: Boolean,
modifier: Modifier = Modifier,
activeColor: Color = MaterialTheme.colorScheme.selected,
inactiveColor: Color = MaterialTheme.colorScheme.error,
) {
Row(
modifier =
modifier
.wrapContentHeight()
.height(IntrinsicSize.Min)
.background(MaterialTheme.colorScheme.primary)
.padding(horizontal = Dimens.sideMargin)
.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically
) {
Box(
modifier =
Modifier.size(Dimens.relayCircleSize)
.background(
color =
when {
active -> activeColor
else -> inactiveColor
},
shape = CircleShape
)
)
Text(
text =
if (active) stringResource(id = R.string.server_ip_overrides_active)
else stringResource(id = R.string.server_ip_overrides_inactive),
color = MaterialTheme.colorScheme.onPrimary,
modifier =
Modifier.weight(1f)
.alpha(
if (active) {
AlphaVisible
} else {
AlphaInactive
}
)
.padding(horizontal = Dimens.smallPadding, vertical = Dimens.mediumPadding)
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ private fun PreviewMullvadModalBottomSheet() {
title = "Select",
)
},
closeBottomSheet = {}
onDismissRequest = {}
)
}
}
Expand All @@ -49,13 +49,13 @@ fun MullvadModalBottomSheet(
sheetState: SheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true),
backgroundColor: Color = MaterialTheme.colorScheme.surfaceContainer,
onBackgroundColor: Color = MaterialTheme.colorScheme.onSurface,
closeBottomSheet: () -> Unit,
onDismissRequest: () -> Unit,
sheetContent: @Composable ColumnScope.() -> Unit
) {
// This is to avoid weird colors in the status bar and the navigation bar
val paddingValues = BottomSheetDefaults.windowInsets.asPaddingValues()
ModalBottomSheet(
onDismissRequest = closeBottomSheet,
onDismissRequest = onDismissRequest,
sheetState = sheetState,
containerColor = backgroundColor,
modifier = modifier,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,9 @@ fun ScaffoldWithTopBarAndDeviceName(
}

@Composable
fun MullvadSnackbar(snackbarData: SnackbarData) {
fun MullvadSnackbar(modifier: Modifier = Modifier, snackbarData: SnackbarData) {
Snackbar(
modifier = modifier,
snackbarData = snackbarData,
containerColor = MaterialTheme.colorScheme.surfaceContainer,
contentColor = MaterialTheme.colorScheme.onSurface,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package net.mullvad.mullvadvpn.compose.dialog

import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.result.EmptyResultBackNavigator
import com.ramcosta.composedestinations.result.ResultBackNavigator
import com.ramcosta.composedestinations.spec.DestinationStyle
import net.mullvad.mullvadvpn.R
import net.mullvad.mullvadvpn.compose.button.NegativeButton
import net.mullvad.mullvadvpn.compose.button.PrimaryButton
import net.mullvad.mullvadvpn.lib.theme.AppTheme
import net.mullvad.mullvadvpn.lib.theme.Dimens

@Preview
@Composable
private fun PreviewResetServerIpOverridesConfirmationDialog() {
AppTheme { ResetServerIpOverridesConfirmationDialog(EmptyResultBackNavigator()) }
}

@Destination(style = DestinationStyle.Dialog::class)
@Composable
fun ResetServerIpOverridesConfirmationDialog(
resultNavigator: ResultBackNavigator<Boolean>,
) {
AlertDialog(
containerColor = MaterialTheme.colorScheme.background,
confirmButton = {
Column(verticalArrangement = Arrangement.spacedBy(Dimens.buttonSpacing)) {
NegativeButton(
modifier = Modifier.fillMaxWidth(),
text = stringResource(id = R.string.server_ip_overrides_reset_reset_button),
onClick = { resultNavigator.navigateBack(result = true) }
)

PrimaryButton(
modifier = Modifier.fillMaxWidth(),
text = stringResource(R.string.cancel),
onClick = resultNavigator::navigateBack
)
}
},
title = {
Text(
text = stringResource(id = R.string.server_ip_overrides_reset_title),
color = MaterialTheme.colorScheme.onPrimary
)
},
text = {
Text(
text = stringResource(id = R.string.server_ip_overrides_reset_body),
color = MaterialTheme.colorScheme.onPrimary,
style = MaterialTheme.typography.bodySmall,
)
},
onDismissRequest = resultNavigator::navigateBack
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package net.mullvad.mullvadvpn.compose.dialog

import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import com.ramcosta.composedestinations.navigation.EmptyDestinationsNavigator
import com.ramcosta.composedestinations.spec.DestinationStyle
import net.mullvad.mullvadvpn.R

@Preview
@Composable
private fun PreviewServerIpOverridesInfoDialog() {
ServerIpOverridesInfoDialog(EmptyDestinationsNavigator)
}

@Destination(style = DestinationStyle.Dialog::class)
@Composable
fun ServerIpOverridesInfoDialog(navigator: DestinationsNavigator) {
InfoDialog(
message =
buildString {
appendLine(stringResource(id = R.string.server_ip_overrides_info_first_paragraph))
appendLine()
appendLine(stringResource(id = R.string.server_ip_overrides_info_second_paragraph))
appendLine()
append(stringResource(id = R.string.server_ip_overrides_info_third_paragraph))
},
onDismiss = navigator::navigateUp
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package net.mullvad.mullvadvpn.compose.screen

import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Close
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.TextField
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.result.ResultBackNavigator
import net.mullvad.mullvadvpn.R
import net.mullvad.mullvadvpn.compose.component.MullvadSmallTopBar
import net.mullvad.mullvadvpn.compose.textfield.mullvadWhiteTextFieldColors
import net.mullvad.mullvadvpn.compose.transitions.DefaultTransition

@Preview
@Composable
private fun PreviewImportOverridesByText() {
ImportOverridesByTextScreen({}, {})
}

@Destination(style = DefaultTransition::class)
@Composable
fun ImportOverridesByText(
resultNavigator: ResultBackNavigator<String>,
) {
ImportOverridesByTextScreen(
onNavigateBack = resultNavigator::navigateBack,
onImportClicked = { resultNavigator.navigateBack(result = it) }
)
}

@Composable
fun ImportOverridesByTextScreen(
onNavigateBack: () -> Unit,
onImportClicked: (String) -> Unit,
) {
var text by remember { mutableStateOf("") }

Scaffold(
topBar = {
MullvadSmallTopBar(
title = stringResource(R.string.import_overrides_text_title),
navigationIcon = {
IconButton(onClick = onNavigateBack) {
Icon(imageVector = Icons.Default.Close, contentDescription = null)
}
},
actions = {
TextButton(
enabled = text.isNotEmpty(),
colors =
ButtonDefaults.textButtonColors()
.copy(contentColor = MaterialTheme.colorScheme.onPrimary),
onClick = { onImportClicked(text) }
) {
Text(
text = stringResource(R.string.import_overrides_import),
)
}
}
)
},
) {
Column(modifier = Modifier.padding(it)) {
TextField(
modifier = Modifier.fillMaxSize(),
value = text,
onValueChange = { text = it },
placeholder = {
Text(text = stringResource(R.string.import_override_textfield_placeholder))
},
colors = mullvadWhiteTextFieldColors()
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -545,7 +545,7 @@ private fun CustomListsBottomSheet(
) {
MullvadModalBottomSheet(
sheetState = sheetState,
closeBottomSheet = { closeBottomSheet(false) },
onDismissRequest = { closeBottomSheet(false) },
modifier = Modifier.testTag(SELECT_LOCATION_CUSTOM_LIST_BOTTOM_SHEET_TEST_TAG)
) { ->
HeaderCell(
Expand Down Expand Up @@ -598,7 +598,7 @@ private fun LocationBottomSheet(
) {
MullvadModalBottomSheet(
sheetState = sheetState,
closeBottomSheet = { closeBottomSheet(false) },
onDismissRequest = { closeBottomSheet(false) },
modifier = Modifier.testTag(SELECT_LOCATION_LOCATION_BOTTOM_SHEET_TEST_TAG)
) { ->
HeaderCell(
Expand Down Expand Up @@ -656,7 +656,7 @@ private fun EditCustomListBottomSheet(
) {
MullvadModalBottomSheet(
sheetState = sheetState,
closeBottomSheet = { closeBottomSheet(false) }
onDismissRequest = { closeBottomSheet(false) }
) {
HeaderCell(text = customList.name, background = Color.Unspecified)
IconCell(
Expand Down
Loading

0 comments on commit ea08149

Please sign in to comment.