diff options
Diffstat (limited to 'android')
7 files changed, 148 insertions, 94 deletions
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/button/MullvadSegmentedButton.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/button/MullvadSegmentedButton.kt index 18ad113a26..093bc06ad2 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/button/MullvadSegmentedButton.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/button/MullvadSegmentedButton.kt @@ -1,14 +1,20 @@ package net.mullvad.mullvadvpn.compose.button import androidx.compose.foundation.BorderStroke +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.ButtonGroup +import androidx.compose.material3.ButtonGroupDefaults +import androidx.compose.material3.ButtonGroupScope +import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.SegmentedButton -import androidx.compose.material3.SegmentedButtonDefaults -import androidx.compose.material3.SingleChoiceSegmentedButtonRow -import androidx.compose.material3.SingleChoiceSegmentedButtonRowScope import androidx.compose.material3.Text +import androidx.compose.material3.ToggleButton +import androidx.compose.material3.ToggleButtonColors +import androidx.compose.material3.ToggleButtonShapes import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Shape import androidx.compose.ui.text.style.TextAlign @@ -16,41 +22,48 @@ 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.color.AlphaDisabled import net.mullvad.mullvadvpn.lib.theme.color.onSelected import net.mullvad.mullvadvpn.lib.theme.color.selected +@OptIn(ExperimentalMaterial3ExpressiveApi::class) @Preview @Composable private fun PreviewMullvadSegmentedButton() { AppTheme { - SingleChoiceSegmentedButtonRow { - MullvadSegmentedStartButton(selected = true, text = "Start", onClick = {}) - MullvadSegmentedMiddleButton(selected = false, text = "Middle", onClick = {}) - MullvadSegmentedEndButton(selected = false, text = "End", onClick = {}) + Row { + MullvadSegmentedStartButton(selected = true, text = "Start", onCheckedChange = {}) + MullvadSegmentedMiddleButton(selected = false, text = "Middle", onCheckedChange = {}) + MullvadSegmentedEndButton(selected = false, text = "End", onCheckedChange = {}) } } } +@OptIn(ExperimentalMaterial3ExpressiveApi::class) @Composable -private fun SingleChoiceSegmentedButtonRowScope.MullvadSegmentedButton( +private fun RowScope.MullvadSegmentedButton( selected: Boolean, text: String, - onClick: () -> Unit, - shape: Shape, + onCheckedChange: (Boolean) -> Unit, + shapes: ToggleButtonShapes, ) { - SegmentedButton( - onClick = onClick, - selected = selected, + ToggleButton( + modifier = Modifier.weight(1f), + onCheckedChange = onCheckedChange, + checked = selected, colors = - SegmentedButtonDefaults.colors() - .copy( - activeContainerColor = MaterialTheme.colorScheme.selected, - activeContentColor = MaterialTheme.colorScheme.onSelected, - inactiveContainerColor = MaterialTheme.colorScheme.primary, - inactiveContentColor = MaterialTheme.colorScheme.onPrimary, - ), + ToggleButtonColors( + checkedContainerColor = MaterialTheme.colorScheme.selected, + checkedContentColor = MaterialTheme.colorScheme.onSelected, + disabledContentColor = + MaterialTheme.colorScheme.onPrimary.copy(alpha = AlphaDisabled), + disabledContainerColor = + MaterialTheme.colorScheme.onPrimary.copy(alpha = AlphaDisabled), + containerColor = MaterialTheme.colorScheme.primary, + contentColor = MaterialTheme.colorScheme.onPrimary, + ), border = BorderStroke(0.dp, Color.Unspecified), - label = { + content = { Text( text = text, textAlign = TextAlign.Center, @@ -59,49 +72,51 @@ private fun SingleChoiceSegmentedButtonRowScope.MullvadSegmentedButton( overflow = TextOverflow.Ellipsis, ) }, - icon = {}, - shape = shape, + shapes = shapes, ) } +@OptIn(ExperimentalMaterial3ExpressiveApi::class) @Composable -fun SingleChoiceSegmentedButtonRowScope.MullvadSegmentedStartButton( +fun RowScope.MullvadSegmentedStartButton( selected: Boolean, text: String, - onClick: () -> Unit, + onCheckedChange: (Boolean) -> Unit, ) { MullvadSegmentedButton( selected = selected, text = text, - onClick = onClick, - shape = RoundedCornerShape(topStart = 8.dp, bottomStart = 8.dp), + onCheckedChange = onCheckedChange, + shapes = ButtonGroupDefaults.connectedLeadingButtonShapes(), ) } +@OptIn(ExperimentalMaterial3ExpressiveApi::class) @Composable -fun SingleChoiceSegmentedButtonRowScope.MullvadSegmentedMiddleButton( +fun RowScope.MullvadSegmentedMiddleButton( selected: Boolean, text: String, - onClick: () -> Unit, + onCheckedChange: (Boolean) -> Unit, ) { MullvadSegmentedButton( selected = selected, text = text, - onClick = onClick, - shape = RoundedCornerShape(0.dp), // Square + onCheckedChange = onCheckedChange, + shapes = ButtonGroupDefaults.connectedMiddleButtonShapes(), ) } +@OptIn(ExperimentalMaterial3ExpressiveApi::class) @Composable -fun SingleChoiceSegmentedButtonRowScope.MullvadSegmentedEndButton( +fun RowScope.MullvadSegmentedEndButton( selected: Boolean, text: String, - onClick: () -> Unit, + onCheckedChange: (Boolean) -> Unit, ) { MullvadSegmentedButton( selected = selected, text = text, - onClick = onClick, - shape = RoundedCornerShape(topEnd = 8.dp, bottomEnd = 8.dp), + onCheckedChange = onCheckedChange, + shapes = ButtonGroupDefaults.connectedTrailingButtonShapes(), ) } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/button/SwitchLocationButton.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/button/SwitchLocationButton.kt index b03a4f9be8..b7704fef48 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/button/SwitchLocationButton.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/button/SwitchLocationButton.kt @@ -6,9 +6,12 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.shape.CornerSize import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.ButtonShapes +import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi import androidx.compose.material3.FilledIconButton import androidx.compose.material3.Icon import androidx.compose.material3.IconButtonDefaults +import androidx.compose.material3.IconButtonShapes import androidx.compose.material3.LocalMinimumInteractiveComponentSize import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text @@ -56,6 +59,7 @@ private fun PreviewConnectionButton() { } } +@OptIn(ExperimentalMaterial3ExpressiveApi::class) @Composable @Suppress("LongMethod") fun SwitchLocationButton( @@ -84,15 +88,26 @@ fun SwitchLocationButton( Button( onClick = onSwitchLocation, - shape = - if (isReconnectButtonEnabled) { - MaterialTheme.shapes.small.copy( - topEnd = CornerSize(percent = 0), - bottomEnd = CornerSize(percent = 0), - ) - } else { - MaterialTheme.shapes.small - }, + shapes = ButtonShapes( + shape = + if (isReconnectButtonEnabled) { + MaterialTheme.shapes.small.copy( + topEnd = CornerSize(percent = 0), + bottomEnd = CornerSize(percent = 0), + ) + } else { + MaterialTheme.shapes.small + }, + pressedShape = + if (isReconnectButtonEnabled) { + MaterialTheme.shapes.extraLarge.copy( + topEnd = CornerSize(percent = 0), + bottomEnd = CornerSize(percent = 0), + ) + } else { + MaterialTheme.shapes.extraLarge + }, + ), colors = ButtonDefaults.buttonColors( containerColor = MaterialTheme.colorScheme.primary, @@ -134,11 +149,13 @@ fun SwitchLocationButton( if (isReconnectButtonEnabled) { FilledIconButton( - shape = - MaterialTheme.shapes.small.copy( - topStart = CornerSize(percent = 0), - bottomStart = CornerSize(percent = 0), - ), + shapes = IconButtonShapes(shape = MaterialTheme.shapes.small.copy( + topStart = CornerSize(percent = 0), + bottomStart = CornerSize(percent = 0), + ), pressedShape = MaterialTheme.shapes.extraLarge.copy( + topStart = CornerSize(percent = 0), + bottomStart = CornerSize(percent = 0), + )), colors = IconButtonDefaults.filledIconButtonColors( containerColor = MaterialTheme.colorScheme.primary, diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/CircularProgressIndicator.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/CircularProgressIndicator.kt index aae37c3f0b..f6bde902a4 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/CircularProgressIndicator.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/CircularProgressIndicator.kt @@ -1,21 +1,21 @@ package net.mullvad.mullvadvpn.compose.component +import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size -import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi +import androidx.compose.material3.LoadingIndicator import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.StrokeCap import androidx.compose.ui.platform.testTag 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 -import net.mullvad.mullvadvpn.lib.theme.color.Alpha20 import net.mullvad.mullvadvpn.lib.ui.tag.CIRCULAR_PROGRESS_INDICATOR_TEST_TAG @Preview @@ -23,7 +23,7 @@ import net.mullvad.mullvadvpn.lib.ui.tag.CIRCULAR_PROGRESS_INDICATOR_TEST_TAG private fun PreviewMullvadProgressIndicator() { AppTheme { Column( - modifier = Modifier.padding(16.dp), + modifier = Modifier.padding(16.dp).background(MaterialTheme.colorScheme.surface), verticalArrangement = Arrangement.spacedBy(16.dp), ) { MullvadCircularProgressIndicatorLarge() @@ -33,54 +33,51 @@ private fun PreviewMullvadProgressIndicator() { } } +@OptIn(ExperimentalMaterial3ExpressiveApi::class) @Composable fun MullvadCircularProgressIndicatorLarge( modifier: Modifier = Modifier, color: Color = MaterialTheme.colorScheme.onSurface, - trackColor: Color = color.copy(alpha = Alpha20), + // trackColor: Color = color.copy(alpha = Alpha20), ) { - CircularProgressIndicator( - modifier - .padding(Dimens.tinyPadding) - .size(Dimens.circularProgressBarLargeSize) - .testTag(CIRCULAR_PROGRESS_INDICATOR_TEST_TAG), - color, - Dimens.circularProgressBarLargeStrokeWidth, - trackColor, - StrokeCap.Round, + LoadingIndicator( + modifier = + modifier + //.padding(Dimens.tinyPadding) + //.size(Dimens.circularProgressBarLargeSize) + .testTag(CIRCULAR_PROGRESS_INDICATOR_TEST_TAG), + color = color, ) } +@OptIn(ExperimentalMaterial3ExpressiveApi::class) @Composable fun MullvadCircularProgressIndicatorMedium( modifier: Modifier = Modifier, color: Color = MaterialTheme.colorScheme.onSurface, - trackColor: Color = color.copy(alpha = Alpha20), + // trackColor: Color = color.copy(alpha = Alpha20), ) { - CircularProgressIndicator( - modifier - .size(Dimens.circularProgressBarMediumSize) - .testTag(CIRCULAR_PROGRESS_INDICATOR_TEST_TAG), - color, - Dimens.circularProgressBarMediumStrokeWidth, - trackColor, - StrokeCap.Round, + LoadingIndicator( + modifier = + modifier + .size(Dimens.circularProgressBarMediumSize) + .testTag(CIRCULAR_PROGRESS_INDICATOR_TEST_TAG), + color = color, ) } +@OptIn(ExperimentalMaterial3ExpressiveApi::class) @Composable fun MullvadCircularProgressIndicatorSmall( modifier: Modifier = Modifier, color: Color = MaterialTheme.colorScheme.onSurface, - trackColor: Color = color.copy(alpha = Alpha20), + // trackColor: Color = color.copy(alpha = Alpha20), ) { - CircularProgressIndicator( - modifier - .size(Dimens.circularProgressBarSmallSize) - .testTag(CIRCULAR_PROGRESS_INDICATOR_TEST_TAG), - color, - Dimens.circularProgressBarSmallStrokeWidth, - trackColor, - StrokeCap.Round, + LoadingIndicator( + modifier = + modifier + .size(Dimens.circularProgressBarSmallSize) + .testTag(CIRCULAR_PROGRESS_INDICATOR_TEST_TAG), + color = color, ) } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/TopBar.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/TopBar.kt index 25bc842870..6da36c7fce 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/TopBar.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/TopBar.kt @@ -21,11 +21,14 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.AccountCircle import androidx.compose.material.icons.filled.Settings import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi import androidx.compose.material3.Icon import androidx.compose.material3.IconButton +import androidx.compose.material3.LargeFlexibleTopAppBar import androidx.compose.material3.LargeTopAppBar import androidx.compose.material3.LocalTextStyle import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.MediumFlexibleTopAppBar import androidx.compose.material3.MediumTopAppBar import androidx.compose.material3.Surface import androidx.compose.material3.Text @@ -107,6 +110,7 @@ private fun PreviewNothingTopBar() { } } +@OptIn(ExperimentalMaterial3ExpressiveApi::class) @Composable fun MullvadTopBar( containerColor: Color, @@ -157,6 +161,7 @@ fun MullvadTopBar( } } }, + subtitle = {}, actions = { if (onAccountClicked != null) { IconButton( @@ -240,6 +245,7 @@ private fun PreviewSlimMediumTopBar() { } } +@OptIn(ExperimentalMaterial3ExpressiveApi::class) @Composable fun MullvadMediumTopBar( title: String, @@ -247,7 +253,7 @@ fun MullvadMediumTopBar( actions: @Composable RowScope.() -> Unit = {}, scrollBehavior: TopAppBarScrollBehavior? = null, ) { - MediumTopAppBar( + MediumFlexibleTopAppBar( title = { Text(text = title, maxLines = 1, overflow = TextOverflow.Ellipsis) }, navigationIcon = navigationIcon, scrollBehavior = scrollBehavior, @@ -261,7 +267,7 @@ fun MullvadMediumTopBar( ) } -@OptIn(ExperimentalMaterial3Api::class) +@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class) @Composable fun MullvadLargeTopBar( title: String, @@ -269,7 +275,7 @@ fun MullvadLargeTopBar( actions: @Composable RowScope.() -> Unit = {}, scrollBehavior: TopAppBarScrollBehavior? = null, ) { - LargeTopAppBar( + LargeFlexibleTopAppBar( title = { Text( text = title, diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/location/SelectLocationScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/location/SelectLocationScreen.kt index ec8c25207b..2a6afac113 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/location/SelectLocationScreen.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/location/SelectLocationScreen.kt @@ -7,6 +7,7 @@ import androidx.compose.foundation.background 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.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth @@ -18,6 +19,8 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Close import androidx.compose.material.icons.filled.FilterList import androidx.compose.material.icons.filled.Search +import androidx.compose.material3.ButtonGroup +import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme @@ -354,9 +357,10 @@ fun SelectLocationScreen( } } +@OptIn(ExperimentalMaterial3ExpressiveApi::class) @Composable private fun MultihopBar(relayListType: RelayListType, onSelectRelayList: (RelayListType) -> Unit) { - SingleChoiceSegmentedButtonRow( + Row( modifier = Modifier.fillMaxWidth() .padding( @@ -367,12 +371,12 @@ private fun MultihopBar(relayListType: RelayListType, onSelectRelayList: (RelayL ) { MullvadSegmentedStartButton( selected = relayListType == RelayListType.ENTRY, - onClick = { onSelectRelayList(RelayListType.ENTRY) }, + onCheckedChange = { onSelectRelayList(RelayListType.ENTRY) }, text = stringResource(id = R.string.entry), ) MullvadSegmentedEndButton( selected = relayListType == RelayListType.EXIT, - onClick = { onSelectRelayList(RelayListType.EXIT) }, + onCheckedChange = { onSelectRelayList(RelayListType.EXIT) }, text = stringResource(id = R.string.exit), ) } diff --git a/android/lib/theme/src/main/kotlin/net/mullvad/mullvadvpn/lib/theme/Theme.kt b/android/lib/theme/src/main/kotlin/net/mullvad/mullvadvpn/lib/theme/Theme.kt index 3257ea2d5b..49dcd86c97 100644 --- a/android/lib/theme/src/main/kotlin/net/mullvad/mullvadvpn/lib/theme/Theme.kt +++ b/android/lib/theme/src/main/kotlin/net/mullvad/mullvadvpn/lib/theme/Theme.kt @@ -3,7 +3,9 @@ package net.mullvad.mullvadvpn.lib.theme import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.ripple.RippleAlpha import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi import androidx.compose.material3.LocalRippleConfiguration +import androidx.compose.material3.MaterialExpressiveTheme import androidx.compose.material3.MaterialTheme import androidx.compose.material3.RippleConfiguration import androidx.compose.material3.Shapes @@ -133,7 +135,7 @@ fun ProvideDimens(dimensions: Dimensions, content: @Composable () -> Unit) { private val LocalAppDimens = staticCompositionLocalOf { defaultDimensions } -@OptIn(ExperimentalMaterial3Api::class) +@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class) @Composable fun AppTheme(content: @Composable () -> Unit) { val typography = MullvadMaterial3Typography @@ -141,7 +143,7 @@ fun AppTheme(content: @Composable () -> Unit) { val dimensions = defaultDimensions ProvideDimens(dimensions = dimensions) { - MaterialTheme( + MaterialExpressiveTheme( colorScheme = darkColorScheme, shapes = Shapes, typography = typography, diff --git a/android/lib/theme/src/main/kotlin/net/mullvad/mullvadvpn/lib/theme/Type.kt b/android/lib/theme/src/main/kotlin/net/mullvad/mullvadvpn/lib/theme/Type.kt index 76b24c1327..be26322fa2 100644 --- a/android/lib/theme/src/main/kotlin/net/mullvad/mullvadvpn/lib/theme/Type.kt +++ b/android/lib/theme/src/main/kotlin/net/mullvad/mullvadvpn/lib/theme/Type.kt @@ -1,5 +1,6 @@ package net.mullvad.mullvadvpn.lib.theme +import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi import androidx.compose.material3.Typography import androidx.compose.ui.text.font.FontWeight @@ -15,12 +16,24 @@ bodySmall (12sp 400 weight) -> Disclaimer texts and error texts under inputs labelLarge (14sp 500 weight) -> Cell that are not header cells, Dialog texts, device name and expiry */ +@OptIn(ExperimentalMaterial3ExpressiveApi::class) internal val MullvadMaterial3Typography = with(Typography()) { this.copy( - headlineLarge = headlineLarge.merge(fontWeight = FontWeight.Bold), - headlineSmall = headlineSmall.merge(fontWeight = FontWeight.SemiBold), - titleLarge = titleLarge.merge(fontWeight = FontWeight.SemiBold), - titleMedium = titleMedium.merge(fontWeight = FontWeight.SemiBold), + displayLarge = displayLargeEmphasized, + displayMedium = displayMediumEmphasized, + displaySmall = displaySmallEmphasized, + headlineLarge = headlineLargeEmphasized.merge(fontWeight = FontWeight.Bold), + headlineMedium = headlineMediumEmphasized, + headlineSmall = headlineSmallEmphasized.merge(fontWeight = FontWeight.SemiBold), + titleLarge = titleLargeEmphasized.merge(fontWeight = FontWeight.SemiBold), + titleMedium = titleMediumEmphasized.merge(fontWeight = FontWeight.SemiBold), + titleSmall = titleSmallEmphasized, + bodyLarge = bodyLargeEmphasized, + bodyMedium = bodyMediumEmphasized, + bodySmall = bodySmallEmphasized, + labelLarge = labelLargeEmphasized, + labelMedium = labelMediumEmphasized, + labelSmall = labelSmallEmphasized, ) } |
