Skip to content

Commit

Permalink
Implement wireguard over shadowsocks
Browse files Browse the repository at this point in the history
  • Loading branch information
Pururun committed Aug 26, 2024
1 parent ec20988 commit 98eadaf
Show file tree
Hide file tree
Showing 32 changed files with 729 additions and 147 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
package net.mullvad.mullvadvpn.compose.cell

import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.IntrinsicSize
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.VerticalDivider
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import net.mullvad.mullvadvpn.R
import net.mullvad.mullvadvpn.compose.preview.SelectObfuscationCellPreviewParameterProvider
import net.mullvad.mullvadvpn.lib.model.Constraint
import net.mullvad.mullvadvpn.lib.model.Port
import net.mullvad.mullvadvpn.lib.model.SelectedObfuscation
import net.mullvad.mullvadvpn.lib.theme.AppTheme
import net.mullvad.mullvadvpn.lib.theme.Dimens
import net.mullvad.mullvadvpn.lib.theme.color.selected
import net.mullvad.mullvadvpn.lib.theme.typeface.listItemSubText
import net.mullvad.mullvadvpn.lib.theme.typeface.listItemText

@Preview
@Composable
private fun PreviewSelectObfuscationCellCell(
@PreviewParameter(SelectObfuscationCellPreviewParameterProvider::class)
selectedObfuscationCellData: Triple<SelectedObfuscation, Constraint<Port>?, Boolean>,
) {
AppTheme {
SelectObfuscationCell(
selectedObfuscation = selectedObfuscationCellData.first,
port = selectedObfuscationCellData.second,
isSelected = selectedObfuscationCellData.third,
onSelected = {},
onNavigate = {}
)
}
}

@Composable
fun SelectObfuscationCell(
selectedObfuscation: SelectedObfuscation,
port: Constraint<Port>? = null,
isSelected: Boolean,
onSelected: (SelectedObfuscation) -> Unit,
onNavigate: () -> Unit = {}
) {
Row(
modifier =
Modifier.height(IntrinsicSize.Min)
.fillMaxWidth()
.background(MaterialTheme.colorScheme.surfaceContainerLow)
) {
TwoRowCell(
modifier = Modifier.weight(1f),
titleStyle = MaterialTheme.typography.listItemText,
titleColor = MaterialTheme.colorScheme.onSurface,
subtitleStyle = MaterialTheme.typography.listItemSubText,
subtitleColor = MaterialTheme.colorScheme.onSurface,
titleText = selectedObfuscation.toTitle(),
subtitleText = port.toSubTitle()?.let { stringResource(id = R.string.port_x, it) },
onCellClicked = { onSelected(selectedObfuscation) },
minHeight = Dimens.cellHeight,
background =
if (isSelected) {
MaterialTheme.colorScheme.selected
} else {
Color.Transparent
},
iconView = {
SelectableIcon(
iconContentDescription = null,
isSelected = isSelected,
isEnabled = true
)
}
)
if (port != null) {
VerticalDivider(
color = Color.Transparent,
modifier =
Modifier.fillMaxHeight().padding(vertical = Dimens.verticalDividerPadding)
)
Icon(
painterResource(id = R.drawable.icon_chevron),
contentDescription = null,
tint = MaterialTheme.colorScheme.onPrimary,
modifier =
Modifier.fillMaxHeight()
.clickable { onNavigate() }
.padding(horizontal = Dimens.obfuscationNavigationPadding)
)
}
}
}

@Composable
private fun SelectedObfuscation.toTitle() =
when (this) {
SelectedObfuscation.Auto -> stringResource(id = R.string.automatic)
SelectedObfuscation.Off -> stringResource(id = R.string.off)
SelectedObfuscation.Udp2Tcp -> stringResource(id = R.string.upd_over_tcp)
SelectedObfuscation.Shadowsocks -> stringResource(id = R.string.shadowsocks)
}

@Composable
private fun Constraint<Port>?.toSubTitle() =
when (this) {
Constraint.Any -> stringResource(id = R.string.automatic)
is Constraint.Only -> this.value.toString()
null -> null
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,16 +42,10 @@ fun SelectableCell(
isEnabled: Boolean = true,
iconContentDescription: String? = null,
selectedIcon: @Composable RowScope.() -> Unit = {
Icon(
painter = painterResource(id = R.drawable.icon_tick),
contentDescription = iconContentDescription,
tint = MaterialTheme.colorScheme.onSelected,
modifier =
Modifier.padding(end = Dimens.selectableCellTextMargin)
.alpha(
if (isSelected && !isEnabled) AlphaDisabled
else if (isSelected) AlphaVisible else AlphaInvisible
)
SelectableIcon(
iconContentDescription = iconContentDescription,
isSelected = isSelected,
isEnabled = isEnabled
)
},
titleStyle: TextStyle = MaterialTheme.typography.labelLarge,
Expand Down Expand Up @@ -98,3 +92,22 @@ fun SelectableCell(
testTag = testTag
)
}

@Composable
fun RowScope.SelectableIcon(
iconContentDescription: String?,
isSelected: Boolean,
isEnabled: Boolean
) {
Icon(
painter = painterResource(id = R.drawable.icon_tick),
contentDescription = iconContentDescription,
tint = MaterialTheme.colorScheme.onSelected,
modifier =
Modifier.padding(end = Dimens.selectableCellTextMargin)
.alpha(
if (isSelected && !isEnabled) AlphaDisabled
else if (isSelected) AlphaVisible else AlphaInvisible
)
)
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package net.mullvad.mullvadvpn.compose.cell

import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
Expand All @@ -11,6 +13,7 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
import net.mullvad.mullvadvpn.lib.theme.AppTheme
import net.mullvad.mullvadvpn.lib.theme.Dimens

Expand All @@ -23,16 +26,22 @@ private fun PreviewTwoRowCell() {
@Composable
fun TwoRowCell(
titleText: String,
subtitleText: String,
subtitleText: String?,
modifier: Modifier = Modifier,
bodyView: @Composable ColumnScope.() -> Unit = {},
iconView: @Composable RowScope.() -> Unit = {},
onCellClicked: () -> Unit = {},
titleColor: Color = MaterialTheme.colorScheme.onPrimary,
subtitleColor: Color = MaterialTheme.colorScheme.onPrimary,
titleStyle: TextStyle = MaterialTheme.typography.labelLarge,
subtitleStyle: TextStyle = MaterialTheme.typography.labelLarge,
background: Color = MaterialTheme.colorScheme.primary
background: Color = MaterialTheme.colorScheme.primary,
isRowEnabled: Boolean = true,
endPadding: Dp = Dimens.cellEndPadding,
minHeight: Dp = Dimens.cellHeightTwoRows,
) {
BaseCell(
modifier = modifier,
headlineContent = {
Column(modifier = Modifier.weight(1f)) {
Text(
Expand All @@ -43,19 +52,24 @@ fun TwoRowCell(
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
Text(
modifier = Modifier.fillMaxWidth(),
text = subtitleText,
style = subtitleStyle,
color = subtitleColor,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
subtitleText?.let {
Text(
modifier = Modifier.fillMaxWidth(),
text = subtitleText,
style = subtitleStyle,
color = subtitleColor,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
}
}
},
bodyView = bodyView,
iconView = iconView,
onCellClicked = onCellClicked,
background = background,
minHeight = Dimens.cellHeightTwoRows
isRowEnabled = isRowEnabled,
minHeight = minHeight,
endPadding = endPadding
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,9 @@ import net.mullvad.mullvadvpn.util.inAnyOf
@Composable
private fun PreviewWireguardCustomPortDialog() {
AppTheme {
WireguardCustomPort(
WireguardCustomPortNavArgs(
CustomPort(
CustomPortNavArgs(
title = "Custom port",
customPort = null,
allowedPortRanges = listOf(PortRange(10..10), PortRange(40..50)),
),
Expand All @@ -49,18 +50,20 @@ private fun PreviewWireguardCustomPortDialog() {
}

@Parcelize
data class WireguardCustomPortNavArgs(
data class CustomPortNavArgs(
val title: String,
val customPort: Port?,
val allowedPortRanges: List<PortRange>,
) : Parcelable

@Destination<RootGraph>(style = DestinationStyle.Dialog::class)
@Composable
fun WireguardCustomPort(
navArg: WireguardCustomPortNavArgs,
fun CustomPort(
navArg: CustomPortNavArgs,
backNavigator: ResultBackNavigator<Port?>,
) {
WireguardCustomPortDialog(
CustomPortDialog(
title = navArg.title,
initialPort = navArg.customPort,
allowedPortRanges = navArg.allowedPortRanges,
onSave = { port -> backNavigator.navigateBack(port) },
Expand All @@ -69,7 +72,8 @@ fun WireguardCustomPort(
}

@Composable
fun WireguardCustomPortDialog(
fun CustomPortDialog(
title: String,
initialPort: Port?,
allowedPortRanges: List<PortRange>,
onSave: (Port?) -> Unit,
Expand All @@ -82,7 +86,7 @@ fun WireguardCustomPortDialog(
AlertDialog(
title = {
Text(
text = stringResource(id = R.string.custom_port_dialog_title),
text = title,
)
},
confirmButton = {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package net.mullvad.mullvadvpn.compose.preview

import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import net.mullvad.mullvadvpn.lib.model.Constraint
import net.mullvad.mullvadvpn.lib.model.Port
import net.mullvad.mullvadvpn.lib.model.SelectedObfuscation

class SelectObfuscationCellPreviewParameterProvider :
PreviewParameterProvider<Triple<SelectedObfuscation, Constraint<Port>?, Boolean>> {
override val values: Sequence<Triple<SelectedObfuscation, Constraint<Port>?, Boolean>> =
sequenceOf(
Triple(SelectedObfuscation.Auto, null, false),
Triple(SelectedObfuscation.Auto, null, true),
Triple(SelectedObfuscation.Shadowsocks, Constraint.Any, false),
Triple(SelectedObfuscation.Shadowsocks, Constraint.Any, true),
Triple(SelectedObfuscation.Shadowsocks, Constraint.Only(Port(PORT)), false),
Triple(SelectedObfuscation.Shadowsocks, Constraint.Only(Port(PORT)), true),
Triple(SelectedObfuscation.Udp2Tcp, Constraint.Any, false),
Triple(SelectedObfuscation.Udp2Tcp, Constraint.Any, true),
Triple(SelectedObfuscation.Udp2Tcp, Constraint.Only(Port(PORT)), false),
Triple(SelectedObfuscation.Udp2Tcp, Constraint.Only(Port(PORT)), true),
Triple(SelectedObfuscation.Off, null, false),
Triple(SelectedObfuscation.Off, null, true),
)
}

private const val PORT = 44
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ import kotlinx.coroutines.launch
import net.mullvad.mullvadvpn.R
import net.mullvad.mullvadvpn.compose.button.PrimaryButton
import net.mullvad.mullvadvpn.compose.button.VariantButton
import net.mullvad.mullvadvpn.compose.cell.BaseCell
import net.mullvad.mullvadvpn.compose.cell.TwoRowCell
import net.mullvad.mullvadvpn.compose.component.MullvadCircularProgressIndicatorLarge
import net.mullvad.mullvadvpn.compose.component.MullvadCircularProgressIndicatorMedium
import net.mullvad.mullvadvpn.compose.component.ScaffoldWithTopBar
Expand Down Expand Up @@ -327,25 +327,13 @@ private fun ColumnScope.DeviceListHeader(state: DeviceListUiState) {

@Composable
private fun DeviceListItem(device: Device, isLoading: Boolean, onDeviceRemovalClicked: () -> Unit) {
BaseCell(
isRowEnabled = false,
headlineContent = {
Column(modifier = Modifier.weight(1f)) {
Text(
modifier = Modifier.fillMaxWidth(),
text = device.displayName(),
style = MaterialTheme.typography.listItemText,
color = MaterialTheme.colorScheme.onPrimary
)
Text(
modifier = Modifier.fillMaxWidth(),
text =
stringResource(id = R.string.created_x, device.creationDate.formatDate()),
style = MaterialTheme.typography.listItemSubText,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
}
},
TwoRowCell(
titleStyle = MaterialTheme.typography.listItemText,
titleColor = MaterialTheme.colorScheme.onPrimary,
subtitleStyle = MaterialTheme.typography.listItemSubText,
subtitleColor = MaterialTheme.colorScheme.onSurfaceVariant,
titleText = device.displayName(),
subtitleText = stringResource(id = R.string.created_x, device.creationDate.formatDate()),
bodyView = {
if (isLoading) {
MullvadCircularProgressIndicatorMedium(
Expand All @@ -362,7 +350,9 @@ private fun DeviceListItem(device: Device, isLoading: Boolean, onDeviceRemovalCl
}
}
},
isRowEnabled = false,
endPadding = Dimens.smallPadding,
minHeight = Dimens.cellHeight
)
}

Expand Down
Loading

0 comments on commit 98eadaf

Please sign in to comment.