diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/BaseCell.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/BaseCell.kt index 262d46e31ca1..46d124865286 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/BaseCell.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/BaseCell.kt @@ -4,8 +4,9 @@ import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ColumnScope import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.defaultMinSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding @@ -20,6 +21,7 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.testTag import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.Dp import net.mullvad.mullvadvpn.compose.component.SpacedColumn @@ -54,9 +56,9 @@ private fun PreviewBaseCell() { @Composable internal fun BaseCell( modifier: Modifier = Modifier, - iconView: @Composable () -> Unit = {}, - title: @Composable () -> Unit, - bodyView: @Composable () -> Unit = {}, + iconView: @Composable RowScope.() -> Unit = {}, + title: @Composable RowScope.() -> Unit, + bodyView: @Composable ColumnScope.() -> Unit = {}, isRowEnabled: Boolean = true, onCellClicked: () -> Unit = {}, background: Color = MaterialTheme.colorScheme.primary, @@ -82,8 +84,6 @@ internal fun BaseCell( title() - Spacer(modifier = Modifier.weight(1.0f)) - Column(modifier = Modifier.wrapContentWidth().wrapContentHeight()) { bodyView() } } } @@ -93,14 +93,16 @@ internal fun BaseCellTitle( title: String, style: TextStyle, modifier: Modifier = Modifier, - textAlign: TextAlign = TextAlign.Center + textAlign: TextAlign = TextAlign.Start ) { Text( text = title, textAlign = textAlign, style = style, color = MaterialTheme.colorScheme.onPrimary, - modifier = modifier.wrapContentWidth(align = Alignment.End).wrapContentHeight() + overflow = TextOverflow.Ellipsis, + maxLines = 1, + modifier = modifier ) } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/ExpandableComposeCell.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/ExpandableComposeCell.kt index c2283d6f472e..347dcef5fb4c 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/ExpandableComposeCell.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/ExpandableComposeCell.kt @@ -57,7 +57,7 @@ fun ExpandableComposeCell( BaseCellTitle( title = title, style = MaterialTheme.typography.titleMedium, - modifier = titleModifier + modifier = titleModifier.weight(1f, fill = true) ) }, bodyView = { diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/InformationComposeCell.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/InformationComposeCell.kt index f6228ec68780..d9e3c9f770d9 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/InformationComposeCell.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/InformationComposeCell.kt @@ -49,7 +49,7 @@ fun InformationComposeCell( BaseCellTitle( title = title, style = MaterialTheme.typography.titleMedium, - modifier = titleModifier + modifier = titleModifier.weight(1f, true) ) }, background = background, diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/MtuComposeCell.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/MtuComposeCell.kt index 1ccd8e8e4f5a..7cd45ddb2d86 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/MtuComposeCell.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/MtuComposeCell.kt @@ -6,10 +6,8 @@ import androidx.compose.foundation.layout.wrapContentWidth as wrapContentWidth1 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.res.stringResource -import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import net.mullvad.mullvadvpn.R import net.mullvad.mullvadvpn.constant.MTU_MAX_VALUE @@ -30,7 +28,7 @@ fun MtuComposeCell( val titleModifier = Modifier BaseCell( - title = { MtuTitle(modifier = titleModifier) }, + title = { MtuTitle(modifier = titleModifier.weight(1f, true)) }, bodyView = { MtuBodyView(mtuValue = mtuValue, modifier = titleModifier) }, onCellClicked = { onEditMtu.invoke() } ) @@ -40,10 +38,9 @@ fun MtuComposeCell( private fun MtuTitle(modifier: Modifier) { Text( text = stringResource(R.string.wireguard_mtu), - textAlign = TextAlign.Center, style = MaterialTheme.typography.titleMedium, color = MaterialTheme.colorScheme.onPrimary, - modifier = modifier.wrapContentWidth1(align = Alignment.End).wrapContentHeight() + modifier = modifier ) } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/NavigationComposeCell.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/NavigationComposeCell.kt index 14cfc9ad1f5f..272e599d8d12 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/NavigationComposeCell.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/NavigationComposeCell.kt @@ -17,41 +17,46 @@ import androidx.compose.ui.res.painterResource 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 @Preview @Composable private fun PreviewNavigationCell() { - NavigationComposeCell( - title = "Navigation sample", - bodyView = { - NavigationCellBody( - contentBodyDescription = "", - content = "content body", - contentColor = MaterialTheme.colorScheme.error, - ) - }, - onClick = {}, - showWarning = true - ) + AppTheme { + NavigationComposeCell( + title = "Navigation sample", + bodyView = { + NavigationCellBody( + contentBodyDescription = "", + content = "content body", + contentColor = MaterialTheme.colorScheme.error, + ) + }, + onClick = {}, + showWarning = true + ) + } } @Preview @Composable private fun PreviewExternalLinkComposeCell() { - NavigationComposeCell( - title = "External link sample", - bodyView = { - NavigationCellBody( - contentBodyDescription = "content body", - content = "content body", - contentColor = MaterialTheme.colorScheme.onSecondary, - isExternalLink = true - ) - }, - onClick = {}, - showWarning = false - ) + AppTheme { + NavigationComposeCell( + title = "External link sample", + bodyView = { + NavigationCellBody( + contentBodyDescription = "content body", + content = "content body", + contentColor = MaterialTheme.colorScheme.onSecondary, + isExternalLink = true + ) + }, + onClick = {}, + showWarning = false + ) + } } @Composable @@ -66,7 +71,11 @@ fun NavigationComposeCell( BaseCell( onCellClicked = onClick, title = { - NavigationTitleView(title = title, modifier = modifier, showWarning = showWarning) + NavigationTitleView( + title = title, + modifier = modifier.weight(1f, true), + showWarning = showWarning + ) }, bodyView = { bodyView() }, isRowEnabled = isRowEnabled @@ -89,7 +98,8 @@ internal fun NavigationTitleView( Text( text = title, style = MaterialTheme.typography.titleMedium, - color = MaterialTheme.colorScheme.onPrimary + color = MaterialTheme.colorScheme.onPrimary, + modifier = modifier ) } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/SelectableCell.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/SelectableCell.kt index 087ba3daf451..686ba8846d8d 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/SelectableCell.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/SelectableCell.kt @@ -1,6 +1,7 @@ package net.mullvad.mullvadvpn.compose.cell import androidx.compose.foundation.background +import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.padding import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme @@ -36,7 +37,7 @@ fun SelectableCell( title: String, isSelected: Boolean, iconContentDescription: String? = null, - selectedIcon: @Composable () -> Unit = { + selectedIcon: @Composable RowScope.() -> Unit = { Icon( painter = painterResource(id = R.drawable.icon_tick), contentDescription = iconContentDescription, 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 02546e37d3b7..28a8bcdfd0a8 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 @@ -2,6 +2,7 @@ package net.mullvad.mullvadvpn.compose.cell import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.foundation.layout.wrapContentWidth @@ -20,7 +21,7 @@ import androidx.compose.ui.unit.dp import androidx.core.text.HtmlCompat import androidx.core.text.HtmlCompat.FROM_HTML_MODE_COMPACT import net.mullvad.mullvadvpn.R -import net.mullvad.mullvadvpn.compose.component.CellSwitch +import net.mullvad.mullvadvpn.compose.component.MullvadSwitch import net.mullvad.mullvadvpn.compose.component.SpacedColumn import net.mullvad.mullvadvpn.compose.component.textResource import net.mullvad.mullvadvpn.compose.extensions.toAnnotatedString @@ -61,7 +62,13 @@ fun NormalSwitchComposeCell( onInfoClicked: (() -> Unit)? = null ) { SwitchComposeCell( - titleView = { BaseCellTitle(title = title, style = MaterialTheme.typography.labelLarge) }, + titleView = { + BaseCellTitle( + title = title, + style = MaterialTheme.typography.labelLarge, + modifier = Modifier.weight(1f, true) + ) + }, isToggled = isToggled, startPadding = startPadding, isEnabled = isEnabled, @@ -83,7 +90,13 @@ fun HeaderSwitchComposeCell( onInfoClicked: (() -> Unit)? = null, ) { SwitchComposeCell( - titleView = { BaseCellTitle(title = title, style = MaterialTheme.typography.titleMedium) }, + titleView = { + BaseCellTitle( + title = title, + style = MaterialTheme.typography.titleMedium, + modifier = Modifier.weight(1f, fill = true) + ) + }, isToggled = isToggled, startPadding = startPadding, isEnabled = isEnabled, @@ -96,7 +109,7 @@ fun HeaderSwitchComposeCell( @Composable private fun SwitchComposeCell( - titleView: @Composable () -> Unit, + titleView: @Composable RowScope.() -> Unit, isToggled: Boolean, startPadding: Dp, isEnabled: Boolean, @@ -154,7 +167,7 @@ fun SwitchCellView( ) } - CellSwitch(isChecked = isToggled, isEnabled = isEnabled, onCheckedChange = onSwitchClicked) + MullvadSwitch(checked = isToggled, enabled = isEnabled, onCheckedChange = onSwitchClicked) } } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/Switch.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/Switch.kt index bc344196c37f..e1ed80db8e66 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/Switch.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/Switch.kt @@ -1,108 +1,95 @@ package net.mullvad.mullvadvpn.compose.component -import androidx.compose.animation.core.animateFloatAsState -import androidx.compose.foundation.Canvas -import androidx.compose.foundation.gestures.detectTapGestures +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.material3.Switch +import androidx.compose.material3.SwitchColors +import androidx.compose.material3.SwitchDefaults import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.scale -import androidx.compose.ui.geometry.CornerRadius -import androidx.compose.ui.geometry.Offset -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.StrokeCap -import androidx.compose.ui.graphics.drawscope.Stroke -import androidx.compose.ui.input.pointer.pointerInput -import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.graphics.compositeOver import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.dp -import net.mullvad.mullvadvpn.lib.theme.color.AlphaInactive -import net.mullvad.mullvadvpn.lib.theme.color.AlphaVisible -import net.mullvad.mullvadvpn.lib.theme.color.MullvadGreen -import net.mullvad.mullvadvpn.lib.theme.color.MullvadRed -import net.mullvad.mullvadvpn.lib.theme.color.MullvadWhite +import net.mullvad.mullvadvpn.lib.theme.AppTheme +import net.mullvad.mullvadvpn.lib.theme.Dimens +import net.mullvad.mullvadvpn.lib.theme.color.AlphaDisabled +import net.mullvad.mullvadvpn.lib.theme.color.selected @Preview @Composable -private fun PreviewSwitch() { - Column { - CellSwitch(isChecked = true, onCheckedChange = null) - CellSwitch(isChecked = false, onCheckedChange = null, isEnabled = false) +private fun PreviewMullvadSwitch() { + AppTheme { + Surface(color = MaterialTheme.colorScheme.background) { + Column( + verticalArrangement = Arrangement.spacedBy(Dimens.mediumPadding), + modifier = Modifier.padding(Dimens.sideMargin) + ) { + MullvadSwitch(checked = true, onCheckedChange = null) + MullvadSwitch(checked = false, onCheckedChange = null) + MullvadSwitch(checked = true, enabled = false, onCheckedChange = null) + MullvadSwitch(checked = false, enabled = false, onCheckedChange = null) + } + } } } @Composable -fun CellSwitch( - isChecked: Boolean, +fun MullvadSwitch( + checked: Boolean, onCheckedChange: ((Boolean) -> Unit)?, modifier: Modifier = Modifier, - scale: Float = 1f, - isEnabled: Boolean = true, - thumbCheckedTrackColor: Color = MullvadGreen, - thumbUncheckedTrackColor: Color = MullvadRed, - thumbColor: Color = MullvadWhite + thumbContent: (@Composable () -> Unit)? = { + // This is needed to ensure the thumb always is big in off mode + Spacer(modifier = Modifier.size(Dimens.switchIconSize)) + }, + enabled: Boolean = true, + colors: SwitchColors = mullvadSwitchColors(), + interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, ) { - val gapBetweenThumbAndTrackEdge: Dp = 2.dp - val width: Dp = 46.dp - val height: Dp = 28.dp - val thumbRadius = 11.dp - - // To move the thumb, we need to calculate the position (along x axis) - val animatePosition = - animateFloatAsState( - targetValue = - if (isChecked) - with(LocalDensity.current) { - (width - thumbRadius - gapBetweenThumbAndTrackEdge - 1.dp).toPx() - } - else - with(LocalDensity.current) { - (thumbRadius + gapBetweenThumbAndTrackEdge + 1.dp).toPx() - } - ) - - Canvas( - modifier = - modifier - .padding(1.dp) - .size(width = width, height = height) - .scale(scale = scale) - .pointerInput(Unit) { - if (onCheckedChange != null && isEnabled) { - detectTapGestures(onTap = { onCheckedChange(!isChecked) }) - } - } - ) { - // Track - drawRoundRect( - color = thumbColor, - alpha = if (isEnabled) AlphaVisible else AlphaInactive, - cornerRadius = CornerRadius(x = 15.dp.toPx(), y = 15.dp.toPx()), - style = - Stroke( - width = 2.dp.toPx(), - miter = 6.dp.toPx(), - cap = StrokeCap.Square, - ) - ) - - // Thumb - drawCircle( - color = if (isChecked) thumbCheckedTrackColor else thumbUncheckedTrackColor, - alpha = if (isEnabled) AlphaVisible else AlphaInactive, - radius = thumbRadius.toPx(), - center = - Offset( - x = animatePosition.value, - y = size.height / 2, - ) - ) - } - - Spacer(modifier = Modifier.height(18.dp)) + Switch( + checked = checked, + onCheckedChange = onCheckedChange, + modifier = modifier, + thumbContent = thumbContent, + enabled = enabled, + colors = colors, + interactionSource + ) } + +@Composable +fun mullvadSwitchColors(): SwitchColors = + SwitchDefaults.colors( + checkedThumbColor = MaterialTheme.colorScheme.selected, + checkedTrackColor = MaterialTheme.colorScheme.primary, + checkedBorderColor = MaterialTheme.colorScheme.onPrimary, + // checkedIconColor= SwitchTokens.SelectedIconColor.toColor(), + uncheckedThumbColor = MaterialTheme.colorScheme.error, + uncheckedTrackColor = MaterialTheme.colorScheme.primary, + uncheckedBorderColor = MaterialTheme.colorScheme.onPrimary, + // uncheckedIconColor= SwitchTokens.UnselectedIconColor.toColor(), + disabledCheckedThumbColor = + MaterialTheme.colorScheme.selected + .copy(alpha = AlphaDisabled) + .compositeOver(MaterialTheme.colorScheme.primary), + disabledCheckedTrackColor = MaterialTheme.colorScheme.primary, + disabledCheckedBorderColor = + MaterialTheme.colorScheme.onPrimary + .copy(alpha = AlphaDisabled) + .compositeOver(MaterialTheme.colorScheme.primary), + disabledUncheckedThumbColor = + MaterialTheme.colorScheme.error + .copy(alpha = AlphaDisabled) + .compositeOver(MaterialTheme.colorScheme.primary), + disabledUncheckedTrackColor = MaterialTheme.colorScheme.primary, + disabledUncheckedBorderColor = + MaterialTheme.colorScheme.onPrimary + .copy(alpha = AlphaDisabled) + .compositeOver(MaterialTheme.colorScheme.primary), + ) diff --git a/android/lib/theme/src/main/kotlin/net/mullvad/mullvadvpn/lib/theme/dimensions/Dimensions.kt b/android/lib/theme/src/main/kotlin/net/mullvad/mullvadvpn/lib/theme/dimensions/Dimensions.kt index 1307b2fae239..ec2c2ff18edf 100644 --- a/android/lib/theme/src/main/kotlin/net/mullvad/mullvadvpn/lib/theme/dimensions/Dimensions.kt +++ b/android/lib/theme/src/main/kotlin/net/mullvad/mullvadvpn/lib/theme/dimensions/Dimensions.kt @@ -55,6 +55,7 @@ data class Dimensions( val smallPadding: Dp = 8.dp, val spacingAboveButton: Dp = 22.dp, val successIconVerticalPadding: Dp = 26.dp, + val switchIconSize: Dp = 24.dp, val titleIconSize: Dp = 24.dp, val topBarHeight: Dp = 64.dp, val verticalSpace: Dp = 20.dp,