summaryrefslogtreecommitdiffhomepage
path: root/android/app/src
diff options
context:
space:
mode:
authorAlbin <albin@mullvad.net>2023-10-06 22:53:58 +0200
committerAlbin <albin@mullvad.net>2023-10-06 22:53:58 +0200
commitc1f9e782da8d8cd5731fe43017f056b184f126c9 (patch)
tree6548308aa794a079b2b36b5e96a6b17e7a8e8002 /android/app/src
parent7256c138ebc59463bfa0771f7c3093bac36a8f09 (diff)
parent7e28f789dadaa07950101058330757769a76e70d (diff)
downloadmullvadvpn-c1f9e782da8d8cd5731fe43017f056b184f126c9.tar.xz
mullvadvpn-c1f9e782da8d8cd5731fe43017f056b184f126c9.zip
Merge branch 'evaluate-replacing-collapsing-toolbar-droid-272'
Diffstat (limited to 'android/app/src')
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/CollapsingToolbarScaffold.kt89
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/CollapsingTopBar.kt112
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/NavigateBackIconButton.kt27
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/Scaffolding.kt130
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/TopBar.kt199
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/AccountScreen.kt240
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/ReportProblemScreen.kt152
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/SettingsScreen.kt65
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/SplitTunnelingScreen.kt223
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/ViewLogsScreen.kt35
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/VpnSettingsScreen.kt71
11 files changed, 560 insertions, 783 deletions
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/CollapsingToolbarScaffold.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/CollapsingToolbarScaffold.kt
deleted file mode 100644
index d1651e542f..0000000000
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/CollapsingToolbarScaffold.kt
+++ /dev/null
@@ -1,89 +0,0 @@
-package net.mullvad.mullvadvpn.compose.component
-
-import androidx.compose.foundation.background
-import androidx.compose.foundation.layout.BoxWithConstraints
-import androidx.compose.foundation.layout.fillMaxHeight
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.height
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableIntStateOf
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.setValue
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.layout.onGloballyPositioned
-import androidx.compose.ui.platform.LocalDensity
-import androidx.compose.ui.unit.dp
-import com.google.accompanist.systemuicontroller.rememberSystemUiController
-import me.onebone.toolbar.CollapsingToolbarScaffold
-import me.onebone.toolbar.CollapsingToolbarScaffoldScope
-import me.onebone.toolbar.CollapsingToolbarScaffoldState
-import me.onebone.toolbar.CollapsingToolbarScope
-import me.onebone.toolbar.ExperimentalToolbarApi
-import me.onebone.toolbar.ScrollStrategy
-
-@OptIn(ExperimentalToolbarApi::class)
-@Composable
-fun CollapsingToolbarScaffold(
- backgroundColor: Color,
- state: CollapsingToolbarScaffoldState,
- modifier: Modifier = Modifier,
- scrollStrategy: ScrollStrategy = ScrollStrategy.ExitUntilCollapsed,
- isEnabledWhenCollapsable: Boolean = true,
- toolbarModifier: Modifier = Modifier,
- toolbar: @Composable CollapsingToolbarScope.() -> Unit,
- body: @Composable CollapsingToolbarScaffoldScope.() -> Unit,
-) {
- val dynamic = remember { mutableStateOf(0.dp) }
- val systemUiController = rememberSystemUiController()
- systemUiController.setNavigationBarColor(backgroundColor)
-
- var isCollapsable by remember { mutableStateOf(false) }
-
- LaunchedEffect(isCollapsable) {
- if (!isCollapsable) {
- state.toolbarState.expand()
- }
- }
-
- val totalHeights = remember { mutableStateOf(0.dp) }
- val localDensity = LocalDensity.current
-
- CollapsingToolbarScaffold(
- modifier =
- modifier
- .background(backgroundColor)
- .fillMaxWidth()
- .fillMaxHeight()
- .onGloballyPositioned { coordinates ->
- totalHeights.value = with(localDensity) { coordinates.size.height.toDp() }
- },
- state = state,
- scrollStrategy = scrollStrategy,
- toolbarModifier =
- toolbarModifier.onGloballyPositioned { coordinates ->
- with(localDensity) {
- dynamic.value = totalHeights.value - coordinates.size.height.toDp()
- }
- },
- enabled = isEnabledWhenCollapsable && isCollapsable,
- toolbar = { toolbar() }
- ) {
- var bodyHeight by remember { mutableIntStateOf(0) }
-
- BoxWithConstraints(
- modifier =
- Modifier.height(dynamic.value).onGloballyPositioned { bodyHeight = it.size.height }
- ) {
- val minMaxToolbarHeightDiff =
- with(state) { toolbarState.maxHeight - toolbarState.minHeight }
- val isContentHigherThanCollapseThreshold =
- with(localDensity) { bodyHeight >= maxHeight.toPx() - minMaxToolbarHeightDiff }
- isCollapsable = isContentHigherThanCollapseThreshold
- body()
- }
- }
-}
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/CollapsingTopBar.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/CollapsingTopBar.kt
deleted file mode 100644
index f2906f1d46..0000000000
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/CollapsingTopBar.kt
+++ /dev/null
@@ -1,112 +0,0 @@
-package net.mullvad.mullvadvpn.compose.component
-
-import androidx.compose.foundation.Image
-import androidx.compose.foundation.background
-import androidx.compose.foundation.layout.Spacer
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.height
-import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.width
-import androidx.compose.foundation.layout.wrapContentHeight
-import androidx.compose.foundation.layout.wrapContentWidth
-import androidx.compose.material3.Button
-import androidx.compose.material3.ButtonDefaults
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Text
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.draw.rotate
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.res.dimensionResource
-import androidx.compose.ui.res.painterResource
-import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.text.TextStyle
-import androidx.compose.ui.text.font.FontWeight
-import androidx.compose.ui.text.style.TextAlign
-import androidx.compose.ui.tooling.preview.Preview
-import androidx.compose.ui.unit.dp
-import androidx.compose.ui.unit.sp
-import net.mullvad.mullvadvpn.R
-import net.mullvad.mullvadvpn.lib.theme.AppTheme
-
-@Preview
-@Composable
-private fun PreviewTopBar() {
- AppTheme {
- CollapsingTopBar(
- backgroundColor = MaterialTheme.colorScheme.secondary,
- onBackClicked = {},
- title = "View title",
- progress = 1.0f,
- modifier = Modifier.height(102.dp)
- )
- }
-}
-
-@Composable
-fun CollapsingTopBar(
- backgroundColor: Color,
- onBackClicked: () -> Unit,
- title: String,
- progress: Float,
- modifier: Modifier,
- backIcon: Int? = null,
- shouldRotateBackButtonDown: Boolean = false
-) {
- val expandedToolbarHeight = dimensionResource(id = R.dimen.expanded_toolbar_height)
- val iconSize = dimensionResource(id = R.dimen.icon_size)
- val sideMargin = dimensionResource(id = R.dimen.side_margin)
- val verticalMargin = dimensionResource(id = R.dimen.cell_label_vertical_padding)
- val maxTopPadding = 48
- val minTopPadding = 14
- val maxTitleSize = 30
- val minTitleSize = 20
-
- Spacer(
- modifier = Modifier.fillMaxWidth().height(expandedToolbarHeight).background(backgroundColor)
- )
-
- Button(
- modifier = Modifier.wrapContentWidth().wrapContentHeight(),
- onClick = onBackClicked,
- colors =
- ButtonDefaults.buttonColors(
- contentColor = Color.White,
- containerColor = backgroundColor
- ),
- shape = MaterialTheme.shapes.small
- ) {
- Image(
- painter = painterResource(id = backIcon ?: R.drawable.icon_back),
- contentDescription = stringResource(id = R.string.back),
- modifier =
- Modifier.rotate(if (shouldRotateBackButtonDown) 270f else 0f)
- .width(iconSize)
- .height(iconSize)
- )
- }
-
- Text(
- text = title,
- style =
- TextStyle(color = Color.White, fontWeight = FontWeight.Bold, textAlign = TextAlign.End),
- modifier =
- modifier.padding(
- start = sideMargin,
- end = sideMargin,
- top = (minTopPadding + (maxTopPadding - minTopPadding) * progress).dp,
- bottom = verticalMargin
- ),
- fontSize =
- topBarSize(
- progress = progress,
- minTitleSize = minTitleSize,
- maxTitleSize = maxTitleSize
- )
- .sp
- )
-}
-
-private fun topBarSize(progress: Float, minTitleSize: Int, maxTitleSize: Int): Float {
- return (minTitleSize + ((maxTitleSize - minTitleSize) * progress))
-}
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/NavigateBackIconButton.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/NavigateBackIconButton.kt
new file mode 100644
index 0000000000..798b5e1574
--- /dev/null
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/NavigateBackIconButton.kt
@@ -0,0 +1,27 @@
+package net.mullvad.mullvadvpn.compose.component
+
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.rotate
+import androidx.compose.ui.res.painterResource
+import net.mullvad.mullvadvpn.R
+
+@Composable
+fun NavigateBackIconButton(onNavigateBack: () -> Unit) {
+ IconButton(onClick = onNavigateBack) {
+ Icon(painter = painterResource(id = R.drawable.icon_back), contentDescription = null)
+ }
+}
+
+@Composable
+fun NavigateBackDownIconButton(onNavigateBack: () -> Unit) {
+ IconButton(onClick = onNavigateBack) {
+ Icon(
+ modifier = Modifier.rotate(-90f),
+ painter = painterResource(id = R.drawable.icon_back),
+ contentDescription = null
+ )
+ }
+}
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/Scaffolding.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/Scaffolding.kt
index 0d032f962a..eb4d0d19a5 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/Scaffolding.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/Scaffolding.kt
@@ -1,32 +1,29 @@
package net.mullvad.mullvadvpn.compose.component
-import androidx.compose.foundation.background
-import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.RowScope
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.lazy.LazyListState
+import androidx.compose.foundation.lazy.rememberLazyListState
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.verticalScroll
+import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Snackbar
import androidx.compose.material3.SnackbarData
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState
+import androidx.compose.material3.TopAppBarDefaults
+import androidx.compose.material3.rememberTopAppBarState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableIntStateOf
-import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
-import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.layout.onGloballyPositioned
-import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.input.nestedscroll.nestedScroll
import com.google.accompanist.systemuicontroller.rememberSystemUiController
-import me.onebone.toolbar.CollapsingToolbarScaffold
-import me.onebone.toolbar.CollapsingToolbarScaffoldScope
-import me.onebone.toolbar.CollapsingToolbarScaffoldState
-import me.onebone.toolbar.CollapsingToolbarScope
-import me.onebone.toolbar.ExperimentalToolbarApi
-import me.onebone.toolbar.ScrollStrategy
import net.mullvad.mullvadvpn.lib.theme.AlphaTopBar
@Composable
@@ -51,8 +48,8 @@ fun ScaffoldWithTopBar(
Scaffold(
modifier = modifier,
topBar = {
- TopBar(
- backgroundColor = topBarColor,
+ MullvadTopBar(
+ containerColor = topBarColor,
iconTintColor = iconTintColor,
onSettingsClicked = onSettingsClicked,
onAccountClicked = onAccountClicked,
@@ -75,50 +72,75 @@ fun MullvadSnackbar(snackbarData: SnackbarData) {
}
@Composable
-@OptIn(ExperimentalToolbarApi::class)
-fun CollapsableAwareToolbarScaffold(
- backgroundColor: Color,
+@OptIn(ExperimentalMaterial3Api::class)
+fun ScaffoldWithMediumTopBar(
+ appBarTitle: String,
modifier: Modifier = Modifier,
- state: CollapsingToolbarScaffoldState,
- scrollStrategy: ScrollStrategy,
- isEnabledWhenCollapsable: Boolean = true,
- toolbarModifier: Modifier = Modifier,
- toolbar: @Composable CollapsingToolbarScope.() -> Unit,
- body: @Composable CollapsingToolbarScaffoldScope.() -> Unit
+ navigationIcon: @Composable () -> Unit = {},
+ actions: @Composable RowScope.() -> Unit = {},
+ lazyListState: LazyListState = rememberLazyListState(),
+ content: @Composable (modifier: Modifier, lazyListState: LazyListState) -> Unit
) {
- val systemUiController = rememberSystemUiController()
- systemUiController.setNavigationBarColor(backgroundColor)
-
- var isCollapsable by remember { mutableStateOf(false) }
- LaunchedEffect(isCollapsable) {
- if (!isCollapsable) {
- state.toolbarState.expand()
+ val appBarState = rememberTopAppBarState()
+ val canScroll = lazyListState.canScrollForward || lazyListState.canScrollBackward
+ val scrollBehavior =
+ TopAppBarDefaults.exitUntilCollapsedScrollBehavior(appBarState, canScroll = { canScroll })
+ Scaffold(
+ modifier = modifier.fillMaxSize().nestedScroll(scrollBehavior.nestedScrollConnection),
+ topBar = {
+ MullvadMediumTopBar(
+ title = appBarTitle,
+ navigationIcon = navigationIcon,
+ actions,
+ scrollBehavior = if (canScroll) scrollBehavior else null
+ )
+ },
+ content = {
+ content(
+ Modifier.fillMaxSize().padding(it).drawVerticalScrollbar(lazyListState),
+ lazyListState
+ )
}
- }
-
- CollapsingToolbarScaffold(
- modifier = modifier.background(backgroundColor),
- state = state,
- scrollStrategy = scrollStrategy,
- enabled = isEnabledWhenCollapsable && isCollapsable,
- toolbarModifier = toolbarModifier,
- toolbar = toolbar,
- body = {
- var bodyHeight by remember { mutableIntStateOf(0) }
+ )
+}
- BoxWithConstraints(
- modifier = Modifier.onGloballyPositioned { bodyHeight = it.size.height }
- ) {
- val minMaxToolbarHeightDiff =
- with(state) { toolbarState.maxHeight - toolbarState.minHeight }
- val isContentHigherThanCollapseThreshold =
- with(LocalDensity.current) {
- bodyHeight > maxHeight.toPx() - minMaxToolbarHeightDiff
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun ScaffoldWithMediumTopBar(
+ appBarTitle: String,
+ modifier: Modifier = Modifier,
+ navigationIcon: @Composable () -> Unit = {},
+ actions: @Composable RowScope.() -> Unit = {},
+ content: @Composable (modifier: Modifier) -> Unit
+) {
+ val appBarState = rememberTopAppBarState()
+ val scrollState = rememberScrollState()
+ val canScroll = scrollState.canScrollForward || scrollState.canScrollBackward
+ val scrollBehavior =
+ TopAppBarDefaults.exitUntilCollapsedScrollBehavior(appBarState, canScroll = { canScroll })
+ Scaffold(
+ modifier = modifier.fillMaxSize().nestedScroll(scrollBehavior.nestedScrollConnection),
+ topBar = {
+ MullvadMediumTopBar(
+ title = appBarTitle,
+ navigationIcon = navigationIcon,
+ actions,
+ scrollBehavior =
+ if (canScroll) {
+ scrollBehavior
+ } else {
+ null
}
- isCollapsable = isContentHigherThanCollapseThreshold
- body()
- }
+ )
+ },
+ content = {
+ content(
+ Modifier.fillMaxSize()
+ .padding(it)
+ .drawVerticalScrollbar(scrollState)
+ .verticalScroll(scrollState)
+ )
}
)
}
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 8f2277ad2a..3c5e0e1bb7 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
@@ -1,23 +1,32 @@
+@file:OptIn(ExperimentalMaterial3Api::class)
+
package net.mullvad.mullvadvpn.compose.component
-import androidx.compose.foundation.Image
-import androidx.compose.foundation.background
-import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.fillMaxHeight
-import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
+import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.MediumTopAppBar
+import androidx.compose.material3.Text
+import androidx.compose.material3.TopAppBar
+import androidx.compose.material3.TopAppBarDefaults
+import androidx.compose.material3.TopAppBarScrollBehavior
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
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.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
import net.mullvad.mullvadvpn.R
import net.mullvad.mullvadvpn.lib.theme.AppTheme
import net.mullvad.mullvadvpn.lib.theme.Dimens
@@ -26,8 +35,21 @@ import net.mullvad.mullvadvpn.lib.theme.Dimens
@Composable
private fun PreviewTopBar() {
AppTheme {
- TopBar(
- backgroundColor = MaterialTheme.colorScheme.inversePrimary,
+ MullvadTopBar(
+ containerColor = MaterialTheme.colorScheme.inversePrimary,
+ iconTintColor = MaterialTheme.colorScheme.onPrimary,
+ onSettingsClicked = null,
+ onAccountClicked = {}
+ )
+ }
+}
+
+@Preview(widthDp = 260)
+@Composable
+private fun PreviewSlimTopBar() {
+ AppTheme {
+ MullvadTopBar(
+ containerColor = MaterialTheme.colorScheme.inversePrimary,
iconTintColor = MaterialTheme.colorScheme.onPrimary,
onSettingsClicked = null,
onAccountClicked = {}
@@ -39,8 +61,8 @@ private fun PreviewTopBar() {
@Composable
private fun PreviewNoIconAndLogoTopBar() {
AppTheme {
- TopBar(
- backgroundColor = MaterialTheme.colorScheme.inversePrimary,
+ MullvadTopBar(
+ containerColor = MaterialTheme.colorScheme.inversePrimary,
iconTintColor = MaterialTheme.colorScheme.onPrimary,
isIconAndLogoVisible = false,
onSettingsClicked = {},
@@ -53,8 +75,8 @@ private fun PreviewNoIconAndLogoTopBar() {
@Composable
private fun PreviewNothingTopBar() {
AppTheme {
- TopBar(
- backgroundColor = MaterialTheme.colorScheme.inversePrimary,
+ MullvadTopBar(
+ containerColor = MaterialTheme.colorScheme.inversePrimary,
iconTintColor = MaterialTheme.colorScheme.onPrimary,
isIconAndLogoVisible = false,
onSettingsClicked = null,
@@ -64,62 +86,123 @@ private fun PreviewNothingTopBar() {
}
@Composable
-fun TopBar(
- backgroundColor: Color,
+fun MullvadTopBar(
+ containerColor: Color,
onSettingsClicked: (() -> Unit)?,
onAccountClicked: (() -> Unit)?,
modifier: Modifier = Modifier,
iconTintColor: Color,
isIconAndLogoVisible: Boolean = true
) {
- Row(
- modifier =
- Modifier.fillMaxWidth()
- .height(Dimens.topBarHeight)
- .background(backgroundColor)
- .then(modifier),
- verticalAlignment = Alignment.CenterVertically
- ) {
- Row(
- Modifier.height(Dimens.topBarHeight).weight(1f).padding(start = Dimens.mediumPadding),
- verticalAlignment = Alignment.CenterVertically
- ) {
+ TopAppBar(
+ modifier = modifier,
+ title = {
if (isIconAndLogoVisible) {
- Image(
- painter = painterResource(id = R.drawable.logo_icon),
- contentDescription = null, // No meaningful user info or action.
- modifier = Modifier.size(Dimens.buttonHeight)
- )
- Icon(
- painter = painterResource(id = R.drawable.logo_text),
- tint = iconTintColor,
- contentDescription = null, // No meaningful user info or action.
- modifier =
- Modifier.padding(start = Dimens.smallPadding).height(Dimens.mediumPadding)
- )
+ Row(verticalAlignment = Alignment.CenterVertically) {
+ Icon(
+ painter = painterResource(id = R.drawable.logo_icon),
+ contentDescription = null, // No meaningful user info or action.
+ modifier = Modifier.size(40.dp),
+ tint = Color.Unspecified
+ )
+ // Dynamically show Mullvad VPN Text if it fits, to avoid overlapping icons.
+ BoxWithConstraints {
+ val logoTextPainter = painterResource(id = R.drawable.logo_text)
+ val logoHeight = Dimens.mediumPadding
+ val logoStartEndPadding = Dimens.mediumPadding
+
+ val shouldShowText =
+ remember(maxWidth) {
+ val logoHeightWidthRatio =
+ logoTextPainter.intrinsicSize.width /
+ logoTextPainter.intrinsicSize.height
+ val expectedLength = logoHeightWidthRatio * logoHeight.value
+ maxWidth > (expectedLength + logoStartEndPadding.value * 2).dp
+ }
+
+ if (shouldShowText) {
+ Icon(
+ painter = painterResource(id = R.drawable.logo_text),
+ tint = iconTintColor,
+ contentDescription = null, // No meaningful user info or action.
+ modifier =
+ Modifier.padding(horizontal = Dimens.mediumPadding)
+ .height(logoHeight)
+ )
+ }
+ }
+ }
+ }
+ },
+ actions = {
+ if (onAccountClicked != null) {
+ IconButton(onClick = onAccountClicked) {
+ Icon(
+ painter = painterResource(R.drawable.icon_account),
+ contentDescription = stringResource(id = R.string.settings_account),
+ )
+ }
+ }
+
+ if (onSettingsClicked != null) {
+ IconButton(onClick = onSettingsClicked) {
+ Icon(
+ painter = painterResource(R.drawable.icon_settings),
+ contentDescription = stringResource(id = R.string.settings),
+ )
+ }
}
- }
+ },
+ colors =
+ TopAppBarDefaults.topAppBarColors(
+ containerColor = containerColor,
+ ),
+ )
+}
- if (onAccountClicked != null) {
- Image(
- painter = painterResource(R.drawable.icon_account),
- contentDescription = stringResource(id = R.string.settings_account),
- modifier =
- Modifier.clickable { onAccountClicked() }
- .fillMaxHeight()
- .padding(horizontal = Dimens.mediumPadding)
- )
- }
+@Preview
+@Composable
+private fun PreviewMediumTopBar() {
+ AppTheme {
+ MullvadMediumTopBar(
+ title = "Title",
+ )
+ }
+}
- if (onSettingsClicked != null) {
- Image(
- painter = painterResource(R.drawable.icon_settings),
- contentDescription = stringResource(id = R.string.settings),
- modifier =
- Modifier.clickable { onSettingsClicked() }
- .fillMaxHeight()
- .padding(horizontal = Dimens.mediumPadding)
- )
- }
+@Preview(widthDp = 260)
+@Composable
+private fun PreviewSlimMediumTopBar() {
+ AppTheme {
+ MullvadMediumTopBar(
+ title = "Long top bar with long title",
+ actions = {
+ IconButton(onClick = {}) {
+ Icon(
+ painter = painterResource(id = R.drawable.icon_settings),
+ contentDescription = null
+ )
+ }
+ }
+ )
}
}
+
+@Composable
+fun MullvadMediumTopBar(
+ title: String,
+ navigationIcon: @Composable () -> Unit = {},
+ actions: @Composable RowScope.() -> Unit = {},
+ scrollBehavior: TopAppBarScrollBehavior? = null
+) {
+ MediumTopAppBar(
+ title = { Text(text = title, maxLines = 1, overflow = TextOverflow.Ellipsis) },
+ navigationIcon = navigationIcon,
+ scrollBehavior = scrollBehavior,
+ colors =
+ TopAppBarDefaults.mediumTopAppBarColors(
+ containerColor = MaterialTheme.colorScheme.background
+ ),
+ actions = actions
+ )
+}
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/AccountScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/AccountScreen.kt
index 6241b3bf04..46ee51640b 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/AccountScreen.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/AccountScreen.kt
@@ -5,16 +5,12 @@ import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
-import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.rememberScrollState
-import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
@@ -32,20 +28,18 @@ import com.google.accompanist.systemuicontroller.rememberSystemUiController
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.asSharedFlow
-import me.onebone.toolbar.ScrollStrategy
-import me.onebone.toolbar.rememberCollapsingToolbarScaffoldState
import net.mullvad.mullvadvpn.R
import net.mullvad.mullvadvpn.compose.button.ActionButton
-import net.mullvad.mullvadvpn.compose.component.CollapsingToolbarScaffold
-import net.mullvad.mullvadvpn.compose.component.CollapsingTopBar
import net.mullvad.mullvadvpn.compose.component.CopyableObfuscationView
import net.mullvad.mullvadvpn.compose.component.InformationView
import net.mullvad.mullvadvpn.compose.component.MissingPolicy
-import net.mullvad.mullvadvpn.compose.component.drawVerticalScrollbar
+import net.mullvad.mullvadvpn.compose.component.NavigateBackIconButton
+import net.mullvad.mullvadvpn.compose.component.ScaffoldWithMediumTopBar
import net.mullvad.mullvadvpn.compose.dialog.DeviceNameInfoDialog
import net.mullvad.mullvadvpn.constant.IS_PLAY_BUILD
import net.mullvad.mullvadvpn.lib.common.util.capitalizeFirstCharOfEachWord
import net.mullvad.mullvadvpn.lib.common.util.openAccountPageInBrowser
+import net.mullvad.mullvadvpn.lib.theme.AppTheme
import net.mullvad.mullvadvpn.lib.theme.Dimens
import net.mullvad.mullvadvpn.util.toExpiryDateString
import net.mullvad.mullvadvpn.viewmodel.AccountUiState
@@ -55,16 +49,18 @@ import net.mullvad.mullvadvpn.viewmodel.AccountViewModel
@Preview
@Composable
private fun PreviewAccountScreen() {
- AccountScreen(
- uiState =
- AccountUiState(
- deviceName = "Test Name",
- accountNumber = "1234123412341234",
- accountExpiry = null
- ),
- uiSideEffect = MutableSharedFlow<AccountViewModel.UiSideEffect>().asSharedFlow(),
- enterTransitionEndAction = MutableSharedFlow()
- )
+ AppTheme {
+ AccountScreen(
+ uiState =
+ AccountUiState(
+ deviceName = "Test Name",
+ accountNumber = "1234123412341234",
+ accountExpiry = null
+ ),
+ uiSideEffect = MutableSharedFlow<AccountViewModel.UiSideEffect>().asSharedFlow(),
+ enterTransitionEndAction = MutableSharedFlow()
+ )
+ }
}
@ExperimentalMaterial3Api
@@ -79,8 +75,6 @@ fun AccountScreen(
onBackClick: () -> Unit = {}
) {
val context = LocalContext.current
- val state = rememberCollapsingToolbarScaffoldState()
- val progress = state.toolbarState.progress
val backgroundColor = MaterialTheme.colorScheme.background
val systemUiController = rememberSystemUiController()
@@ -93,133 +87,73 @@ fun AccountScreen(
DeviceNameInfoDialog { showDeviceNameInfoDialog = false }
}
- CollapsingToolbarScaffold(
- backgroundColor = MaterialTheme.colorScheme.background,
- modifier = Modifier.fillMaxSize(),
- state = state,
- scrollStrategy = ScrollStrategy.ExitUntilCollapsed,
- isEnabledWhenCollapsable = false,
- toolbar = {
- val scaffoldModifier =
- Modifier.road(
- whenCollapsed = Alignment.TopCenter,
- whenExpanded = Alignment.BottomStart
- )
- CollapsingTopBar(
- backgroundColor = MaterialTheme.colorScheme.secondary,
- onBackClicked = onBackClick,
- title = stringResource(id = R.string.settings_account),
- progress = progress,
- modifier = scaffoldModifier,
- shouldRotateBackButtonDown = true
- )
- },
- ) {
- LaunchedEffect(Unit) {
- uiSideEffect.collect { uiSideEffect ->
- if (
- uiSideEffect is AccountViewModel.UiSideEffect.OpenAccountManagementPageInBrowser
- ) {
- context.openAccountPageInBrowser(uiSideEffect.token)
- }
+ LaunchedEffect(Unit) {
+ uiSideEffect.collect { uiSideEffect ->
+ if (uiSideEffect is AccountViewModel.UiSideEffect.OpenAccountManagementPageInBrowser) {
+ context.openAccountPageInBrowser(uiSideEffect.token)
}
}
+ }
- val scrollState = rememberScrollState()
-
- Surface(modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background) {
- Column(
- verticalArrangement = Arrangement.Bottom,
- horizontalAlignment = Alignment.Start,
- modifier =
- Modifier.fillMaxSize()
- .drawVerticalScrollbar(scrollState)
- .verticalScroll(scrollState)
- .animateContentSize()
- ) {
- Text(
- style = MaterialTheme.typography.labelMedium,
- text = stringResource(id = R.string.device_name),
- modifier = Modifier.padding(start = Dimens.sideMargin, end = Dimens.sideMargin)
- )
-
- Row(verticalAlignment = Alignment.CenterVertically) {
- InformationView(
- content = uiState.deviceName?.capitalizeFirstCharOfEachWord() ?: "",
- whenMissing = MissingPolicy.SHOW_SPINNER
- )
- IconButton(
- modifier = Modifier.align(Alignment.CenterVertically),
- onClick = { showDeviceNameInfoDialog = true }
- ) {
- Icon(
- painter = painterResource(id = R.drawable.icon_info),
- contentDescription = null,
- tint = MaterialTheme.colorScheme.inverseSurface
- )
- }
- }
-
- Text(
- style = MaterialTheme.typography.labelMedium,
- text = stringResource(id = R.string.account_number),
- modifier =
- Modifier.padding(
- start = Dimens.sideMargin,
- end = Dimens.sideMargin,
- top = Dimens.smallPadding
- )
- )
- CopyableObfuscationView(content = uiState.accountNumber ?: "")
- Text(
- style = MaterialTheme.typography.labelMedium,
- text = stringResource(id = R.string.paid_until),
- modifier = Modifier.padding(start = Dimens.sideMargin, end = Dimens.sideMargin)
- )
+ ScaffoldWithMediumTopBar(
+ appBarTitle = stringResource(id = R.string.settings_account),
+ navigationIcon = { NavigateBackIconButton(onBackClick) }
+ ) { modifier ->
+ Column(
+ verticalArrangement = Arrangement.Bottom,
+ horizontalAlignment = Alignment.Start,
+ modifier = modifier.animateContentSize()
+ ) {
+ Text(
+ style = MaterialTheme.typography.labelMedium,
+ text = stringResource(id = R.string.device_name),
+ modifier = Modifier.padding(start = Dimens.sideMargin, end = Dimens.sideMargin)
+ )
+ Row(verticalAlignment = Alignment.CenterVertically) {
InformationView(
- content = uiState.accountExpiry?.toExpiryDateString() ?: "",
+ content = uiState.deviceName?.capitalizeFirstCharOfEachWord() ?: "",
whenMissing = MissingPolicy.SHOW_SPINNER
)
-
- Spacer(modifier = Modifier.weight(1f))
- if (IS_PLAY_BUILD.not()) {
- ActionButton(
- text = stringResource(id = R.string.manage_account),
- onClick = onManageAccountClick,
- modifier =
- Modifier.padding(
- start = Dimens.sideMargin,
- end = Dimens.sideMargin,
- bottom = Dimens.screenVerticalMargin
- ),
- colors =
- ButtonDefaults.buttonColors(
- contentColor = MaterialTheme.colorScheme.onPrimary,
- containerColor = MaterialTheme.colorScheme.surface
- )
+ IconButton(
+ modifier = Modifier.align(Alignment.CenterVertically),
+ onClick = { showDeviceNameInfoDialog = true }
+ ) {
+ Icon(
+ painter = painterResource(id = R.drawable.icon_info),
+ contentDescription = null,
+ tint = MaterialTheme.colorScheme.inverseSurface
)
}
+ }
- ActionButton(
- text = stringResource(id = R.string.redeem_voucher),
- onClick = onRedeemVoucherClick,
- modifier =
- Modifier.padding(
- start = Dimens.sideMargin,
- end = Dimens.sideMargin,
- bottom = Dimens.screenVerticalMargin
- ),
- colors =
- ButtonDefaults.buttonColors(
- contentColor = MaterialTheme.colorScheme.onPrimary,
- containerColor = MaterialTheme.colorScheme.surface
- )
- )
+ Text(
+ style = MaterialTheme.typography.labelMedium,
+ text = stringResource(id = R.string.account_number),
+ modifier =
+ Modifier.padding(
+ start = Dimens.sideMargin,
+ end = Dimens.sideMargin,
+ top = Dimens.smallPadding
+ )
+ )
+ CopyableObfuscationView(content = uiState.accountNumber ?: "")
+ Text(
+ style = MaterialTheme.typography.labelMedium,
+ text = stringResource(id = R.string.paid_until),
+ modifier = Modifier.padding(start = Dimens.sideMargin, end = Dimens.sideMargin)
+ )
+
+ InformationView(
+ content = uiState.accountExpiry?.toExpiryDateString() ?: "",
+ whenMissing = MissingPolicy.SHOW_SPINNER
+ )
+ Spacer(modifier = Modifier.weight(1f))
+ if (IS_PLAY_BUILD.not()) {
ActionButton(
- text = stringResource(id = R.string.log_out),
- onClick = onLogoutClick,
+ text = stringResource(id = R.string.manage_account),
+ onClick = onManageAccountClick,
modifier =
Modifier.padding(
start = Dimens.sideMargin,
@@ -229,10 +163,42 @@ fun AccountScreen(
colors =
ButtonDefaults.buttonColors(
contentColor = MaterialTheme.colorScheme.onPrimary,
- containerColor = MaterialTheme.colorScheme.error
+ containerColor = MaterialTheme.colorScheme.surface
)
)
}
+
+ ActionButton(
+ text = stringResource(id = R.string.redeem_voucher),
+ onClick = onRedeemVoucherClick,
+ modifier =
+ Modifier.padding(
+ start = Dimens.sideMargin,
+ end = Dimens.sideMargin,
+ bottom = Dimens.screenVerticalMargin
+ ),
+ colors =
+ ButtonDefaults.buttonColors(
+ contentColor = MaterialTheme.colorScheme.onPrimary,
+ containerColor = MaterialTheme.colorScheme.surface
+ )
+ )
+
+ ActionButton(
+ text = stringResource(id = R.string.log_out),
+ onClick = onLogoutClick,
+ modifier =
+ Modifier.padding(
+ start = Dimens.sideMargin,
+ end = Dimens.sideMargin,
+ bottom = Dimens.screenVerticalMargin
+ ),
+ colors =
+ ButtonDefaults.buttonColors(
+ contentColor = MaterialTheme.colorScheme.onPrimary,
+ containerColor = MaterialTheme.colorScheme.error
+ )
+ )
}
}
}
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/ReportProblemScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/ReportProblemScreen.kt
index 4d524c28dc..6dc34937d5 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/ReportProblemScreen.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/ReportProblemScreen.kt
@@ -3,8 +3,8 @@ package net.mullvad.mullvadvpn.compose.screen
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope
+import androidx.compose.foundation.layout.IntrinsicSize
import androidx.compose.foundation.layout.Spacer
-import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
@@ -13,7 +13,6 @@ import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.runtime.Composable
@@ -33,12 +32,10 @@ import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.withStyle
import androidx.compose.ui.tooling.preview.Preview
-import me.onebone.toolbar.ScrollStrategy
-import me.onebone.toolbar.rememberCollapsingToolbarScaffoldState
import net.mullvad.mullvadvpn.R
import net.mullvad.mullvadvpn.compose.button.ActionButton
-import net.mullvad.mullvadvpn.compose.component.CollapsingToolbarScaffold
-import net.mullvad.mullvadvpn.compose.component.CollapsingTopBar
+import net.mullvad.mullvadvpn.compose.component.NavigateBackIconButton
+import net.mullvad.mullvadvpn.compose.component.ScaffoldWithMediumTopBar
import net.mullvad.mullvadvpn.compose.dialog.ReportProblemNoEmailDialog
import net.mullvad.mullvadvpn.compose.textfield.mullvadWhiteTextFieldColors
import net.mullvad.mullvadvpn.dataproxy.SendProblemReportResult
@@ -100,39 +97,30 @@ fun ReportProblemScreen(
onNavigateToViewLogs: () -> Unit = {},
onBackClick: () -> Unit = {}
) {
+ var email by rememberSaveable { mutableStateOf("") }
+ var description by rememberSaveable { mutableStateOf("") }
- val scaffoldState = rememberCollapsingToolbarScaffoldState()
- val progress = scaffoldState.toolbarState.progress
- CollapsingToolbarScaffold(
- backgroundColor = MaterialTheme.colorScheme.background,
- modifier = Modifier.fillMaxSize(),
- state = scaffoldState,
- scrollStrategy = ScrollStrategy.ExitUntilCollapsed,
- isEnabledWhenCollapsable = false,
- toolbar = {
- val scaffoldModifier =
- Modifier.road(
- whenCollapsed = Alignment.TopCenter,
- whenExpanded = Alignment.BottomStart
- )
- CollapsingTopBar(
- backgroundColor = MaterialTheme.colorScheme.background,
- onBackClicked = onBackClick,
- title = stringResource(id = R.string.report_a_problem),
- progress = progress,
- modifier = scaffoldModifier,
- )
- },
- ) {
- var email by rememberSaveable { mutableStateOf("") }
- var description by rememberSaveable { mutableStateOf("") }
+ // Dialog to show confirm if no email was added
+ if (uiState.showConfirmNoEmail) {
+ ReportProblemNoEmailDialog(
+ onDismiss = onDismissNoEmailDialog,
+ onConfirm = { onSendReport(email, description) }
+ )
+ }
+
+ ScaffoldWithMediumTopBar(
+ appBarTitle = stringResource(id = R.string.report_a_problem),
+ navigationIcon = { NavigateBackIconButton(onBackClick) }
+ ) { modifier ->
// Show sending states
if (uiState.sendingState != null) {
Column(
modifier =
- Modifier.fillMaxSize()
- .padding(vertical = Dimens.mediumPadding, horizontal = Dimens.sideMargin)
+ modifier.padding(
+ vertical = Dimens.mediumPadding,
+ horizontal = Dimens.sideMargin
+ )
) {
when (uiState.sendingState) {
SendingReportUiState.Sending -> SendingContent()
@@ -140,69 +128,61 @@ fun ReportProblemScreen(
ErrorContent({ onSendReport(email, description) }, onClearSendResult)
is SendingReportUiState.Success -> SentContent(uiState.sendingState)
}
- return@CollapsingToolbarScaffold
+ return@ScaffoldWithMediumTopBar
}
}
- // Dialog to show confirm if no email was added
- if (uiState.showConfirmNoEmail) {
- ReportProblemNoEmailDialog(
- onDismiss = onDismissNoEmailDialog,
- onConfirm = { onSendReport(email, description) }
- )
- }
-
- Surface(color = MaterialTheme.colorScheme.background) {
- Column(
- modifier =
- Modifier.padding(
+ Column(
+ modifier =
+ modifier
+ .padding(
start = Dimens.sideMargin,
end = Dimens.sideMargin,
bottom = Dimens.verticalSpace,
- ),
- verticalArrangement = Arrangement.spacedBy(Dimens.mediumPadding)
- ) {
- Text(text = stringResource(id = R.string.problem_report_description))
+ )
+ .height(IntrinsicSize.Max),
+ verticalArrangement = Arrangement.spacedBy(Dimens.mediumPadding)
+ ) {
+ Text(text = stringResource(id = R.string.problem_report_description))
- TextField(
- modifier = Modifier.fillMaxWidth(),
- value = email,
- onValueChange = { email = it },
- maxLines = 1,
- singleLine = true,
- placeholder = { Text(text = stringResource(id = R.string.user_email_hint)) },
- colors = mullvadWhiteTextFieldColors()
- )
+ TextField(
+ modifier = Modifier.fillMaxWidth(),
+ value = email,
+ onValueChange = { email = it },
+ maxLines = 1,
+ singleLine = true,
+ placeholder = { Text(text = stringResource(id = R.string.user_email_hint)) },
+ colors = mullvadWhiteTextFieldColors()
+ )
- TextField(
- modifier = Modifier.fillMaxWidth().weight(1f),
- value = description,
- onValueChange = { description = it },
- placeholder = { Text(stringResource(R.string.user_message_hint)) },
- colors = mullvadWhiteTextFieldColors()
- )
+ TextField(
+ modifier = Modifier.fillMaxWidth().weight(1f),
+ value = description,
+ onValueChange = { description = it },
+ placeholder = { Text(stringResource(R.string.user_message_hint)) },
+ colors = mullvadWhiteTextFieldColors()
+ )
- ActionButton(
- colors =
- ButtonDefaults.buttonColors(
- containerColor = MaterialTheme.colorScheme.primary,
- contentColor = MaterialTheme.colorScheme.onPrimary
- ),
- onClick = onNavigateToViewLogs,
- text = stringResource(id = R.string.view_logs)
- )
+ ActionButton(
+ colors =
+ ButtonDefaults.buttonColors(
+ containerColor = MaterialTheme.colorScheme.primary,
+ contentColor = MaterialTheme.colorScheme.onPrimary
+ ),
+ onClick = onNavigateToViewLogs,
+ text = stringResource(id = R.string.view_logs)
+ )
- ActionButton(
- colors =
- ButtonDefaults.buttonColors(
- containerColor = MaterialTheme.colorScheme.surface,
- contentColor = MaterialTheme.colorScheme.onSurface
- ),
- onClick = { onSendReport(email, description) },
- isEnabled = description.isNotEmpty(),
- text = stringResource(id = R.string.send)
- )
- }
+ ActionButton(
+ colors =
+ ButtonDefaults.buttonColors(
+ containerColor = MaterialTheme.colorScheme.surface,
+ contentColor = MaterialTheme.colorScheme.onSurface
+ ),
+ onClick = { onSendReport(email, description) },
+ isEnabled = description.isNotEmpty(),
+ text = stringResource(id = R.string.send)
+ )
}
}
}
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/SettingsScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/SettingsScreen.kt
index f17548eeb6..8983e6ac4a 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/SettingsScreen.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/SettingsScreen.kt
@@ -4,19 +4,14 @@ import android.net.Uri
import androidx.compose.animation.animateContentSize
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Spacer
-import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.lazy.LazyColumn
-import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.testTag
@@ -25,20 +20,18 @@ import androidx.compose.ui.tooling.preview.Preview
import com.google.accompanist.systemuicontroller.rememberSystemUiController
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
-import me.onebone.toolbar.ScrollStrategy
-import me.onebone.toolbar.rememberCollapsingToolbarScaffoldState
import net.mullvad.mullvadvpn.R
import net.mullvad.mullvadvpn.compose.cell.DefaultExternalLinkView
import net.mullvad.mullvadvpn.compose.cell.NavigationCellBody
import net.mullvad.mullvadvpn.compose.cell.NavigationComposeCell
-import net.mullvad.mullvadvpn.compose.component.CollapsableAwareToolbarScaffold
-import net.mullvad.mullvadvpn.compose.component.CollapsingTopBar
-import net.mullvad.mullvadvpn.compose.component.drawVerticalScrollbar
+import net.mullvad.mullvadvpn.compose.component.NavigateBackDownIconButton
+import net.mullvad.mullvadvpn.compose.component.ScaffoldWithMediumTopBar
import net.mullvad.mullvadvpn.compose.extensions.itemWithDivider
import net.mullvad.mullvadvpn.compose.state.SettingsUiState
import net.mullvad.mullvadvpn.compose.test.LAZY_LIST_TEST_TAG
import net.mullvad.mullvadvpn.constant.IS_PLAY_BUILD
import net.mullvad.mullvadvpn.lib.common.util.openLink
+import net.mullvad.mullvadvpn.lib.theme.AppTheme
import net.mullvad.mullvadvpn.lib.theme.Dimens
import net.mullvad.mullvadvpn.util.appendHideNavOnPlayBuild
@@ -46,11 +39,17 @@ import net.mullvad.mullvadvpn.util.appendHideNavOnPlayBuild
@Preview
@Composable
private fun PreviewSettings() {
- SettingsScreen(
- uiState =
- SettingsUiState(appVersion = "2222.22", isLoggedIn = true, isUpdateAvailable = true),
- enterTransitionEndAction = MutableSharedFlow()
- )
+ AppTheme {
+ SettingsScreen(
+ uiState =
+ SettingsUiState(
+ appVersion = "2222.22",
+ isLoggedIn = true,
+ isUpdateAvailable = true
+ ),
+ enterTransitionEndAction = MutableSharedFlow()
+ )
+ }
}
@ExperimentalMaterial3Api
@@ -64,9 +63,6 @@ fun SettingsScreen(
onBackClick: () -> Unit = {}
) {
val context = LocalContext.current
- val lazyListState = rememberLazyListState()
- val state = rememberCollapsingToolbarScaffoldState()
- val progress = state.toolbarState.progress
val backgroundColor = MaterialTheme.colorScheme.background
val systemUiController = rememberSystemUiController()
@@ -74,35 +70,12 @@ fun SettingsScreen(
enterTransitionEndAction.collect { systemUiController.setStatusBarColor(backgroundColor) }
}
- CollapsableAwareToolbarScaffold(
- backgroundColor = MaterialTheme.colorScheme.background,
- modifier = Modifier.fillMaxSize(),
- state = state,
- scrollStrategy = ScrollStrategy.ExitUntilCollapsed,
- isEnabledWhenCollapsable = true,
- toolbar = {
- val scaffoldModifier =
- Modifier.road(
- whenCollapsed = Alignment.TopCenter,
- whenExpanded = Alignment.BottomStart
- )
- CollapsingTopBar(
- backgroundColor = MaterialTheme.colorScheme.secondary,
- onBackClicked = { onBackClick() },
- title = stringResource(id = R.string.settings),
- progress = progress,
- modifier = scaffoldModifier,
- shouldRotateBackButtonDown = true
- )
- },
- ) {
+ ScaffoldWithMediumTopBar(
+ appBarTitle = stringResource(id = R.string.settings),
+ navigationIcon = { NavigateBackDownIconButton(onBackClick) },
+ ) { modifier, lazyListState ->
LazyColumn(
- modifier =
- Modifier.drawVerticalScrollbar(lazyListState)
- .testTag(LAZY_LIST_TEST_TAG)
- .fillMaxWidth()
- .wrapContentHeight()
- .animateContentSize(),
+ modifier = modifier.testTag(LAZY_LIST_TEST_TAG).animateContentSize(),
state = lazyListState
) {
item { Spacer(modifier = Modifier.height(Dimens.cellLabelVerticalPadding)) }
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/SplitTunnelingScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/SplitTunnelingScreen.kt
index ad9a7d3af2..bf47a7a17f 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/SplitTunnelingScreen.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/SplitTunnelingScreen.kt
@@ -11,10 +11,8 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
-import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
@@ -24,16 +22,13 @@ import androidx.compose.ui.graphics.StrokeCap
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
-import me.onebone.toolbar.ScrollStrategy
-import me.onebone.toolbar.rememberCollapsingToolbarScaffoldState
import net.mullvad.mullvadvpn.R
import net.mullvad.mullvadvpn.applist.AppData
import net.mullvad.mullvadvpn.compose.cell.BaseCell
import net.mullvad.mullvadvpn.compose.cell.HeaderSwitchComposeCell
import net.mullvad.mullvadvpn.compose.cell.SplitTunnelingCell
-import net.mullvad.mullvadvpn.compose.component.CollapsingToolbarScaffold
-import net.mullvad.mullvadvpn.compose.component.CollapsingTopBar
-import net.mullvad.mullvadvpn.compose.component.drawVerticalScrollbar
+import net.mullvad.mullvadvpn.compose.component.NavigateBackIconButton
+import net.mullvad.mullvadvpn.compose.component.ScaffoldWithMediumTopBar
import net.mullvad.mullvadvpn.compose.constant.CommonContentKey
import net.mullvad.mullvadvpn.compose.constant.ContentType
import net.mullvad.mullvadvpn.compose.constant.SplitTunnelingContentKey
@@ -86,132 +81,52 @@ fun SplitTunnelingScreen(
onBackClick: () -> Unit = {},
onResolveIcon: (String) -> Bitmap? = { null },
) {
- val state = rememberCollapsingToolbarScaffoldState()
- val progress = state.toolbarState.progress
- val lazyListState = rememberLazyListState()
val focusManager = LocalFocusManager.current
- CollapsingToolbarScaffold(
- backgroundColor = MaterialTheme.colorScheme.background,
+ ScaffoldWithMediumTopBar(
modifier = Modifier.fillMaxSize(),
- state = state,
- scrollStrategy = ScrollStrategy.ExitUntilCollapsed,
- isEnabledWhenCollapsable = true,
- toolbar = {
- val scaffoldModifier =
- Modifier.road(
- whenCollapsed = Alignment.TopCenter,
- whenExpanded = Alignment.BottomStart
- )
- CollapsingTopBar(
- backgroundColor = MaterialTheme.colorScheme.background,
- onBackClicked = { onBackClick() },
- title = stringResource(id = R.string.split_tunneling),
- progress = progress,
- modifier = scaffoldModifier
- )
- },
- ) {
- Surface(color = MaterialTheme.colorScheme.background) {
- LazyColumn(
- modifier = Modifier.drawVerticalScrollbar(state = lazyListState).fillMaxWidth(),
- horizontalAlignment = Alignment.CenterHorizontally,
- state = lazyListState
- ) {
- item(key = CommonContentKey.DESCRIPTION, contentType = ContentType.DESCRIPTION) {
- Box(modifier = Modifier.fillMaxWidth()) {
- Text(
- style = MaterialTheme.typography.labelMedium,
- text = stringResource(id = R.string.split_tunneling_description),
- modifier =
- Modifier.padding(
- start = Dimens.mediumPadding,
- end = Dimens.mediumPadding,
- bottom = Dimens.mediumPadding
- )
+ appBarTitle = stringResource(id = R.string.split_tunneling),
+ navigationIcon = { NavigateBackIconButton(onBackClick) }
+ ) { modifier, lazyListState ->
+ LazyColumn(
+ modifier = modifier,
+ horizontalAlignment = Alignment.CenterHorizontally,
+ state = lazyListState
+ ) {
+ item(key = CommonContentKey.DESCRIPTION, contentType = ContentType.DESCRIPTION) {
+ Box(modifier = Modifier.fillMaxWidth()) {
+ Text(
+ style = MaterialTheme.typography.labelMedium,
+ text = stringResource(id = R.string.split_tunneling_description),
+ modifier =
+ Modifier.padding(
+ start = Dimens.mediumPadding,
+ end = Dimens.mediumPadding,
+ bottom = Dimens.mediumPadding
+ )
+ )
+ }
+ }
+ when (uiState) {
+ SplitTunnelingUiState.Loading -> {
+ item(key = CommonContentKey.PROGRESS, contentType = ContentType.PROGRESS) {
+ CircularProgressIndicator(
+ color = MaterialTheme.colorScheme.onBackground,
+ modifier = Modifier.size(Dimens.progressIndicatorSize),
+ strokeCap = StrokeCap.Round
)
}
}
- when (uiState) {
- SplitTunnelingUiState.Loading -> {
- item(key = CommonContentKey.PROGRESS, contentType = ContentType.PROGRESS) {
- CircularProgressIndicator(
- color = MaterialTheme.colorScheme.onBackground,
- modifier = Modifier.size(Dimens.progressIndicatorSize),
- strokeCap = StrokeCap.Round
- )
- }
- }
- is SplitTunnelingUiState.ShowAppList -> {
- if (uiState.excludedApps.isNotEmpty()) {
- itemWithDivider(
- key = SplitTunnelingContentKey.EXCLUDED_APPLICATIONS,
- contentType = ContentType.HEADER
- ) {
- BaseCell(
- title = {
- Text(
- text =
- stringResource(id = R.string.exclude_applications),
- style = MaterialTheme.typography.titleMedium,
- color = MaterialTheme.colorScheme.onPrimary
- )
- },
- bodyView = {},
- background = MaterialTheme.colorScheme.primary,
- )
- }
- itemsIndexed(
- items = uiState.excludedApps,
- key = { _, listItem -> listItem.packageName },
- contentType = { _, _ -> ContentType.ITEM }
- ) { index, listItem ->
- SplitTunnelingCell(
- title = listItem.name,
- packageName = listItem.packageName,
- isSelected = true,
- modifier = Modifier.animateItemPlacement().fillMaxWidth(),
- onResolveIcon = onResolveIcon
- ) {
- // Move focus down unless the clicked item was the last in this
- // section.
- if (index < uiState.excludedApps.size - 1) {
- focusManager.moveFocus(FocusDirection.Down)
- } else {
- focusManager.moveFocus(FocusDirection.Up)
- }
-
- onIncludeAppClick(listItem.packageName)
- }
- }
- item(key = CommonContentKey.SPACER, contentType = ContentType.SPACER) {
- Spacer(
- modifier =
- Modifier.animateItemPlacement().height(Dimens.mediumPadding)
- )
- }
- }
-
+ is SplitTunnelingUiState.ShowAppList -> {
+ if (uiState.excludedApps.isNotEmpty()) {
itemWithDivider(
- key = SplitTunnelingContentKey.SHOW_SYSTEM_APPLICATIONS,
- contentType = ContentType.OTHER_ITEM
- ) {
- HeaderSwitchComposeCell(
- title = stringResource(id = R.string.show_system_apps),
- isToggled = uiState.showSystemApps,
- onCellClicked = { newValue -> onShowSystemAppsClick(newValue) },
- modifier = Modifier.animateItemPlacement()
- )
- }
- itemWithDivider(
- key = SplitTunnelingContentKey.INCLUDED_APPLICATIONS,
+ key = SplitTunnelingContentKey.EXCLUDED_APPLICATIONS,
contentType = ContentType.HEADER
) {
BaseCell(
- modifier = Modifier.animateItemPlacement(),
title = {
Text(
- text = stringResource(id = R.string.all_applications),
+ text = stringResource(id = R.string.exclude_applications),
style = MaterialTheme.typography.titleMedium,
color = MaterialTheme.colorScheme.onPrimary
)
@@ -221,28 +136,86 @@ fun SplitTunnelingScreen(
)
}
itemsIndexed(
- items = uiState.includedApps,
+ items = uiState.excludedApps,
key = { _, listItem -> listItem.packageName },
contentType = { _, _ -> ContentType.ITEM }
) { index, listItem ->
SplitTunnelingCell(
title = listItem.name,
packageName = listItem.packageName,
- isSelected = false,
+ isSelected = true,
modifier = Modifier.animateItemPlacement().fillMaxWidth(),
onResolveIcon = onResolveIcon
) {
// Move focus down unless the clicked item was the last in this
// section.
- if (index < uiState.includedApps.size - 1) {
+ if (index < uiState.excludedApps.size - 1) {
focusManager.moveFocus(FocusDirection.Down)
} else {
focusManager.moveFocus(FocusDirection.Up)
}
- onExcludeAppClick(listItem.packageName)
+ onIncludeAppClick(listItem.packageName)
}
}
+ item(key = CommonContentKey.SPACER, contentType = ContentType.SPACER) {
+ Spacer(
+ modifier =
+ Modifier.animateItemPlacement().height(Dimens.mediumPadding)
+ )
+ }
+ }
+
+ itemWithDivider(
+ key = SplitTunnelingContentKey.SHOW_SYSTEM_APPLICATIONS,
+ contentType = ContentType.OTHER_ITEM
+ ) {
+ HeaderSwitchComposeCell(
+ title = stringResource(id = R.string.show_system_apps),
+ isToggled = uiState.showSystemApps,
+ onCellClicked = { newValue -> onShowSystemAppsClick(newValue) },
+ modifier = Modifier.animateItemPlacement()
+ )
+ }
+ itemWithDivider(
+ key = SplitTunnelingContentKey.INCLUDED_APPLICATIONS,
+ contentType = ContentType.HEADER
+ ) {
+ BaseCell(
+ modifier = Modifier.animateItemPlacement(),
+ title = {
+ Text(
+ text = stringResource(id = R.string.all_applications),
+ style = MaterialTheme.typography.titleMedium,
+ color = MaterialTheme.colorScheme.onPrimary
+ )
+ },
+ bodyView = {},
+ background = MaterialTheme.colorScheme.primary,
+ )
+ }
+ itemsIndexed(
+ items = uiState.includedApps,
+ key = { _, listItem -> listItem.packageName },
+ contentType = { _, _ -> ContentType.ITEM }
+ ) { index, listItem ->
+ SplitTunnelingCell(
+ title = listItem.name,
+ packageName = listItem.packageName,
+ isSelected = false,
+ modifier = Modifier.animateItemPlacement().fillMaxWidth(),
+ onResolveIcon = onResolveIcon
+ ) {
+ // Move focus down unless the clicked item was the last in this
+ // section.
+ if (index < uiState.includedApps.size - 1) {
+ focusManager.moveFocus(FocusDirection.Down)
+ } else {
+ focusManager.moveFocus(FocusDirection.Up)
+ }
+
+ onExcludeAppClick(listItem.packageName)
+ }
}
}
}
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/ViewLogsScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/ViewLogsScreen.kt
index 091d19f480..cdef4bdbb3 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/ViewLogsScreen.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/ViewLogsScreen.kt
@@ -8,7 +8,9 @@ import androidx.compose.foundation.text.selection.SelectionContainer
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Card
import androidx.compose.material3.CircularProgressIndicator
+import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Scaffold
import androidx.compose.material3.TextField
import androidx.compose.material3.TextFieldDefaults
import androidx.compose.runtime.Composable
@@ -17,11 +19,9 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
-import me.onebone.toolbar.ScrollStrategy
-import me.onebone.toolbar.rememberCollapsingToolbarScaffoldState
import net.mullvad.mullvadvpn.R
-import net.mullvad.mullvadvpn.compose.component.CollapsingToolbarScaffold
-import net.mullvad.mullvadvpn.compose.component.CollapsingTopBar
+import net.mullvad.mullvadvpn.compose.component.MullvadMediumTopBar
+import net.mullvad.mullvadvpn.compose.component.NavigateBackIconButton
import net.mullvad.mullvadvpn.compose.component.drawVerticalScrollbar
import net.mullvad.mullvadvpn.lib.theme.AppTheme
import net.mullvad.mullvadvpn.lib.theme.Dimens
@@ -39,38 +39,25 @@ private fun PreviewViewLogsLoadingScreen() {
AppTheme { ViewLogsScreen(uiState = ViewLogsUiState()) }
}
+@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ViewLogsScreen(
uiState: ViewLogsUiState,
onBackClick: () -> Unit = {},
) {
- val scaffoldState = rememberCollapsingToolbarScaffoldState()
- val progress = scaffoldState.toolbarState.progress
- CollapsingToolbarScaffold(
- backgroundColor = MaterialTheme.colorScheme.background,
- modifier = Modifier.fillMaxSize(),
- state = scaffoldState,
- scrollStrategy = ScrollStrategy.ExitUntilCollapsed,
- isEnabledWhenCollapsable = false,
- toolbar = {
- val scaffoldModifier =
- Modifier.road(
- whenCollapsed = Alignment.TopCenter,
- whenExpanded = Alignment.BottomStart
- )
- CollapsingTopBar(
- backgroundColor = MaterialTheme.colorScheme.secondary,
- onBackClicked = onBackClick,
+ Scaffold(
+ topBar = {
+ MullvadMediumTopBar(
title = stringResource(id = R.string.view_logs),
- progress = progress,
- modifier = scaffoldModifier,
+ navigationIcon = { NavigateBackIconButton(onBackClick) }
)
- },
+ }
) {
Card(
modifier =
Modifier.fillMaxSize()
+ .padding(it)
.padding(
start = Dimens.sideMargin,
end = Dimens.sideMargin,
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/VpnSettingsScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/VpnSettingsScreen.kt
index 41751ad02d..c65710f17b 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/VpnSettingsScreen.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/VpnSettingsScreen.kt
@@ -5,14 +5,10 @@ import androidx.compose.animation.animateContentSize
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Spacer
-import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
-import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material3.Divider
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
@@ -23,7 +19,6 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
-import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
@@ -39,8 +34,6 @@ import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.distinctUntilChanged
-import me.onebone.toolbar.ScrollStrategy
-import me.onebone.toolbar.rememberCollapsingToolbarScaffoldState
import net.mullvad.mullvadvpn.R
import net.mullvad.mullvadvpn.compose.cell.BaseCell
import net.mullvad.mullvadvpn.compose.cell.ContentBlockersDisableModeCellSubtitle
@@ -55,9 +48,8 @@ import net.mullvad.mullvadvpn.compose.cell.MtuSubtitle
import net.mullvad.mullvadvpn.compose.cell.NormalSwitchComposeCell
import net.mullvad.mullvadvpn.compose.cell.SelectableCell
import net.mullvad.mullvadvpn.compose.cell.SwitchComposeSubtitleCell
-import net.mullvad.mullvadvpn.compose.component.CollapsingToolbarScaffold
-import net.mullvad.mullvadvpn.compose.component.CollapsingTopBar
-import net.mullvad.mullvadvpn.compose.component.drawVerticalScrollbar
+import net.mullvad.mullvadvpn.compose.component.NavigateBackIconButton
+import net.mullvad.mullvadvpn.compose.component.ScaffoldWithMediumTopBar
import net.mullvad.mullvadvpn.compose.dialog.ContentBlockersInfoDialog
import net.mullvad.mullvadvpn.compose.dialog.CustomDnsInfoDialog
import net.mullvad.mullvadvpn.compose.dialog.CustomPortDialog
@@ -246,12 +238,9 @@ fun VpnSettingsScreen(
}
}
- val lazyListState = rememberLazyListState()
var expandContentBlockersState by rememberSaveable { mutableStateOf(false) }
val biggerPadding = 54.dp
val topPadding = 6.dp
- val state = rememberCollapsingToolbarScaffoldState()
- val progress = state.toolbarState.progress
LaunchedEffect(uiState.selectedWireguardPort) {
if (
@@ -262,49 +251,27 @@ fun VpnSettingsScreen(
}
}
- CollapsingToolbarScaffold(
- backgroundColor = MaterialTheme.colorScheme.background,
- modifier = Modifier.fillMaxSize(),
- state = state,
- scrollStrategy = ScrollStrategy.ExitUntilCollapsed,
- isEnabledWhenCollapsable = true,
- toolbar = {
- val scaffoldModifier =
- Modifier.road(
- whenCollapsed = Alignment.TopCenter,
- whenExpanded = Alignment.BottomStart
- )
- CollapsingTopBar(
- backgroundColor = MaterialTheme.colorScheme.background,
- onBackClicked = { onBackClick() },
- title = stringResource(id = R.string.settings_vpn),
- progress = progress,
- modifier = scaffoldModifier
- )
- },
- ) {
- val context = LocalContext.current
- LaunchedEffect(Unit) {
- toastMessagesSharedFlow.distinctUntilChanged().collect { message ->
- Toast.makeText(context, message, Toast.LENGTH_SHORT).show()
- }
+ val context = LocalContext.current
+ LaunchedEffect(Unit) {
+ toastMessagesSharedFlow.distinctUntilChanged().collect { message ->
+ Toast.makeText(context, message, Toast.LENGTH_SHORT).show()
}
- DisposableEffect(lifecycleOwner) {
- val observer = LifecycleEventObserver { _, event ->
- if (event == Lifecycle.Event.ON_STOP) {
- onStopEvent()
- }
+ }
+ DisposableEffect(lifecycleOwner) {
+ val observer = LifecycleEventObserver { _, event ->
+ if (event == Lifecycle.Event.ON_STOP) {
+ onStopEvent()
}
- lifecycleOwner.lifecycle.addObserver(observer)
- onDispose { lifecycleOwner.lifecycle.removeObserver(observer) }
}
+ lifecycleOwner.lifecycle.addObserver(observer)
+ onDispose { lifecycleOwner.lifecycle.removeObserver(observer) }
+ }
+ ScaffoldWithMediumTopBar(
+ appBarTitle = stringResource(id = R.string.settings_vpn),
+ navigationIcon = { NavigateBackIconButton(onBackClick) },
+ ) { modifier, lazyListState ->
LazyColumn(
- modifier =
- Modifier.drawVerticalScrollbar(lazyListState)
- .testTag(LAZY_LIST_TEST_TAG)
- .fillMaxWidth()
- .wrapContentHeight()
- .animateContentSize(),
+ modifier = modifier.testTag(LAZY_LIST_TEST_TAG).animateContentSize(),
state = lazyListState
) {
item {