fix(ui): Draw all screens edge-to-edge under system bars

Scrolling content now slides under the transparent status and
navigation bars instead of clipping at the inset boundary. Adds
PaddingValues.plus and systemBarsAndCutoutInsets helpers, moves inset
consumption from scroll viewports into content padding on every screen,
fixes reorder auto-scroll thresholds for content padding, adds IME
handling to form screens, and removes the unused EdgeToEdgeHelper.
This commit is contained in:
darken
2026-07-24 17:53:13 +02:00
committed by Matthias Urhahn
parent 37278af34e
commit 73dc10aee9
21 changed files with 179 additions and 113 deletions
+1 -1
View File
@@ -87,7 +87,7 @@ The monitor-layer glue (`monitor/core/aap/`) described above wires this stack in
### Common Utilities ### Common Utilities
- `EdgeToEdgeHelper`: Handles Android edge-to-edge display insets - `common/compose/InsetsExtensions.kt`: `PaddingValues.plus` operator and `systemBarsAndCutoutInsets` for laying out non-Scaffold screens edge-to-edge
## Build Configuration ## Build Configuration
@@ -5,11 +5,17 @@ import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.WindowInsetsSides
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.only
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.safeDrawing
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
@@ -133,10 +139,12 @@ fun SupporterStatusScreen(
Scaffold( Scaffold(
containerColor = MaterialTheme.colorScheme.surface, containerColor = MaterialTheme.colorScheme.surface,
) { paddingValues -> ) { paddingValues ->
Box(modifier = Modifier.padding(paddingValues)) { Box(modifier = Modifier.fillMaxSize()) {
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxWidth()
.verticalScroll(rememberScrollState()) .verticalScroll(rememberScrollState())
.padding(paddingValues)
.padding(horizontal = 24.dp), .padding(horizontal = 24.dp),
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
) { ) {
@@ -238,6 +246,7 @@ fun SupporterStatusScreen(
onClick = onNavigateUp, onClick = onNavigateUp,
modifier = Modifier modifier = Modifier
.align(Alignment.TopStart) .align(Alignment.TopStart)
.windowInsetsPadding(WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Start))
.padding(4.dp), .padding(4.dp),
) { ) {
Icon( Icon(
@@ -272,10 +281,12 @@ fun UpgradeScreen(
snackbarHost = { SnackbarHost(snackbarHostState) }, snackbarHost = { SnackbarHost(snackbarHostState) },
containerColor = MaterialTheme.colorScheme.surface, containerColor = MaterialTheme.colorScheme.surface,
) { paddingValues -> ) { paddingValues ->
Box(modifier = Modifier.padding(paddingValues)) { Box(modifier = Modifier.fillMaxSize()) {
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxWidth()
.verticalScroll(rememberScrollState()) .verticalScroll(rememberScrollState())
.padding(paddingValues)
.padding(horizontal = 24.dp), .padding(horizontal = 24.dp),
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
) { ) {
@@ -402,6 +413,7 @@ fun UpgradeScreen(
onClick = onNavigateUp, onClick = onNavigateUp,
modifier = Modifier modifier = Modifier
.align(Alignment.TopStart) .align(Alignment.TopStart)
.windowInsetsPadding(WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Start))
.padding(4.dp), .padding(4.dp),
) { ) {
Icon( Icon(
@@ -8,12 +8,17 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.WindowInsetsSides
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.only
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.safeDrawing
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.widthIn import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
@@ -105,7 +110,7 @@ internal fun UpgradeScreenContainer(
modifier = modifier, modifier = modifier,
containerColor = MaterialTheme.colorScheme.surface, containerColor = MaterialTheme.colorScheme.surface,
) { paddingValues -> ) { paddingValues ->
Box(modifier = Modifier.padding(paddingValues)) { Box(modifier = Modifier.fillMaxSize()) {
Box( Box(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
@@ -114,6 +119,7 @@ internal fun UpgradeScreenContainer(
) { ) {
Column( Column(
modifier = Modifier modifier = Modifier
.padding(paddingValues)
// widthIn BEFORE fillMaxWidth: reversed, fillMaxWidth would pin the min to // widthIn BEFORE fillMaxWidth: reversed, fillMaxWidth would pin the min to
// the full screen and the 560dp cap would never take effect on wide screens. // the full screen and the 560dp cap would never take effect on wide screens.
.widthIn(max = 560.dp) .widthIn(max = 560.dp)
@@ -129,6 +135,7 @@ internal fun UpgradeScreenContainer(
onClick = onNavigateUp, onClick = onNavigateUp,
modifier = Modifier modifier = Modifier
.align(Alignment.TopStart) .align(Alignment.TopStart)
.windowInsetsPadding(WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Start))
.padding(4.dp), .padding(4.dp),
) { ) {
// Matches capod's app-wide back-button convention (no navigate-up string exists). // Matches capod's app-wide back-button convention (no navigate-up string exists).
@@ -1,34 +0,0 @@
package eu.darken.capod.common
import android.app.Activity
import android.view.View
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import eu.darken.capod.common.debug.logging.logTag
class EdgeToEdgeHelper(activity: Activity) {
private val tag = logTag("EdgeToEdge", "$activity")
fun insetsPadding(
view: View,
left: Boolean = false,
top: Boolean = false,
right: Boolean = false,
bottom: Boolean = false,
) {
ViewCompat.setOnApplyWindowInsetsListener(view) { v: View, insets: WindowInsetsCompat ->
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
val displayCutout = insets.getInsets(WindowInsetsCompat.Type.displayCutout())
v.setPadding(
if (left) maxOf(systemBars.left, displayCutout.left) else v.paddingLeft,
if (top) maxOf(systemBars.top, displayCutout.top) else v.paddingTop,
if (right) maxOf(systemBars.right, displayCutout.right) else v.paddingRight,
if (bottom) maxOf(systemBars.bottom, displayCutout.bottom) else v.paddingBottom,
)
insets
}
}
}
@@ -0,0 +1,22 @@
package eu.darken.capod.common.compose
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.displayCutout
import androidx.compose.foundation.layout.systemBars
import androidx.compose.foundation.layout.union
import androidx.compose.runtime.Composable
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.LayoutDirection
operator fun PaddingValues.plus(other: PaddingValues): PaddingValues = object : PaddingValues {
override fun calculateLeftPadding(layoutDirection: LayoutDirection): Dp =
this@plus.calculateLeftPadding(layoutDirection) + other.calculateLeftPadding(layoutDirection)
override fun calculateTopPadding(): Dp = this@plus.calculateTopPadding() + other.calculateTopPadding()
override fun calculateRightPadding(layoutDirection: LayoutDirection): Dp =
this@plus.calculateRightPadding(layoutDirection) + other.calculateRightPadding(layoutDirection)
override fun calculateBottomPadding(): Dp = this@plus.calculateBottomPadding() + other.calculateBottomPadding()
}
val systemBarsAndCutoutInsets: WindowInsets
@Composable get() = WindowInsets.systemBars.union(WindowInsets.displayCutout)
@@ -127,16 +127,16 @@ fun <T> ReorderableAutoScroll(state: ReorderableState<T>) {
if (!state.isDragging) return@LaunchedEffect if (!state.isDragging) return@LaunchedEffect
while (isActive) { while (isActive) {
val layoutInfo = state.lazyListState.layoutInfo val layoutInfo = state.lazyListState.layoutInfo
val viewportStart = layoutInfo.viewportStartOffset val contentStart = layoutInfo.viewportStartOffset + layoutInfo.beforeContentPadding
val viewportEnd = layoutInfo.viewportEndOffset val contentEnd = layoutInfo.viewportEndOffset - layoutInfo.afterContentPadding
val draggedIdx = state.draggedIndex ?: break val draggedIdx = state.draggedIndex ?: break
val draggedItem = layoutInfo.visibleItemsInfo.firstOrNull { it.index == draggedIdx } val draggedItem = layoutInfo.visibleItemsInfo.firstOrNull { it.index == draggedIdx }
if (draggedItem != null) { if (draggedItem != null) {
val itemCenter = draggedItem.offset + draggedItem.size / 2 + state.dragOffsetY val itemCenter = draggedItem.offset + draggedItem.size / 2 + state.dragOffsetY
val scrollSpeed = 8f val scrollSpeed = 8f
val delta = when { val delta = when {
itemCenter < viewportStart + scrollThresholdPx -> -scrollSpeed itemCenter < contentStart + scrollThresholdPx -> -scrollSpeed
itemCenter > viewportEnd - scrollThresholdPx -> scrollSpeed itemCenter > contentEnd - scrollThresholdPx -> scrollSpeed
else -> 0f else -> 0f
} }
if (delta != 0f) state.lazyListState.scrollBy(delta) if (delta != 0f) state.lazyListState.scrollBy(delta)
@@ -8,7 +8,6 @@ import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.WindowInsetsSides import androidx.compose.foundation.layout.WindowInsetsSides
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
@@ -16,7 +15,6 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.only import androidx.compose.foundation.layout.only
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.systemBars
import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.windowInsetsPadding import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
@@ -54,6 +52,7 @@ import androidx.compose.ui.unit.dp
import eu.darken.capod.R import eu.darken.capod.R
import eu.darken.capod.common.compose.Preview2 import eu.darken.capod.common.compose.Preview2
import eu.darken.capod.common.compose.PreviewWrapper import eu.darken.capod.common.compose.PreviewWrapper
import eu.darken.capod.common.compose.systemBarsAndCutoutInsets
import java.io.File import java.io.File
@Composable @Composable
@@ -68,16 +67,12 @@ fun RecorderScreen(
val context = LocalContext.current val context = LocalContext.current
Box(modifier = modifier.fillMaxSize()) { Box(modifier = modifier.fillMaxSize()) {
if (state.isWorking) { Column(modifier = Modifier.fillMaxSize()) {
LinearProgressIndicator(modifier = Modifier.fillMaxWidth())
}
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxSize() .weight(1f)
.windowInsetsPadding(WindowInsets.systemBars.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal))
.verticalScroll(rememberScrollState()) .verticalScroll(rememberScrollState())
.padding(bottom = 80.dp), .windowInsetsPadding(systemBarsAndCutoutInsets.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal)),
) { ) {
// Hero section // Hero section
HeroSection() HeroSection()
@@ -109,9 +104,13 @@ fun RecorderScreen(
onDiscard = onDiscard, onDiscard = onDiscard,
onKeep = onKeep, onKeep = onKeep,
onShare = onShare, onShare = onShare,
modifier = Modifier.align(Alignment.BottomCenter),
) )
} }
if (state.isWorking) {
LinearProgressIndicator(modifier = Modifier.fillMaxWidth())
}
}
} }
@Composable @Composable
@@ -373,7 +372,9 @@ private fun BottomActionBar(
color = MaterialTheme.colorScheme.surfaceContainerHigh, color = MaterialTheme.colorScheme.surfaceContainerHigh,
) { ) {
Row( Row(
modifier = Modifier.padding(12.dp), modifier = Modifier
.windowInsetsPadding(systemBarsAndCutoutInsets.only(WindowInsetsSides.Bottom + WindowInsetsSides.Horizontal))
.padding(12.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp), horizontalArrangement = Arrangement.spacedBy(8.dp),
) { ) {
OutlinedButton( OutlinedButton(
@@ -415,7 +416,6 @@ private fun BottomActionBar(
} }
} }
} }
Spacer(modifier = Modifier.windowInsetsPadding(WindowInsets.systemBars.only(WindowInsetsSides.Bottom)))
} }
} }
@@ -4,6 +4,7 @@ import android.content.Intent
import android.provider.Settings import android.provider.Settings
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
@@ -270,7 +271,8 @@ fun DeviceSettingsScreen(
snackbarHost = { SnackbarHost(hostState = snackbarHostState) }, snackbarHost = { SnackbarHost(hostState = snackbarHostState) },
) { paddingValues -> ) { paddingValues ->
LazyColumn( LazyColumn(
modifier = Modifier.padding(paddingValues), modifier = Modifier.fillMaxSize(),
contentPadding = paddingValues,
) { ) {
// Device Info // Device Info
if (device != null) { if (device != null) {
@@ -50,9 +50,9 @@ fun OnboardingScreen(
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
.verticalScroll(rememberScrollState())
.padding(innerPadding) .padding(innerPadding)
.padding(horizontal = 32.dp) .padding(horizontal = 32.dp),
.verticalScroll(rememberScrollState()),
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
) { ) {
Spacer(modifier = Modifier.height(48.dp)) Spacer(modifier = Modifier.height(48.dp))
@@ -6,7 +6,6 @@ import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.lazy.itemsIndexed
@@ -48,6 +47,7 @@ import eu.darken.capod.R
import eu.darken.capod.common.SystemTimeSource import eu.darken.capod.common.SystemTimeSource
import eu.darken.capod.common.compose.Preview2 import eu.darken.capod.common.compose.Preview2
import eu.darken.capod.common.compose.PreviewWrapper import eu.darken.capod.common.compose.PreviewWrapper
import eu.darken.capod.common.compose.plus
import eu.darken.capod.common.compose.preview.MockPodDataProvider import eu.darken.capod.common.compose.preview.MockPodDataProvider
import eu.darken.capod.common.error.ErrorEventHandler import eu.darken.capod.common.error.ErrorEventHandler
import eu.darken.capod.common.navigation.NavigationEventHandler import eu.darken.capod.common.navigation.NavigationEventHandler
@@ -278,10 +278,8 @@ fun OverviewScreen(
snackbarHost = { SnackbarHost(hostState = snackbarHostState) }, snackbarHost = { SnackbarHost(hostState = snackbarHostState) },
) { innerPadding -> ) { innerPadding ->
LazyColumn( LazyColumn(
modifier = Modifier modifier = Modifier.fillMaxSize(),
.fillMaxSize() contentPadding = innerPadding + PaddingValues(horizontal = 8.dp, vertical = 4.dp),
.padding(innerPadding),
contentPadding = PaddingValues(horizontal = 8.dp, vertical = 4.dp),
) { ) {
// 1. Permission cards // 1. Permission cards
items( items(
@@ -1,6 +1,7 @@
package eu.darken.capod.main.ui.presscontrols package eu.darken.capod.main.ui.presscontrols
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
@@ -176,7 +177,10 @@ fun PressControlsScreen(
) )
}, },
) { paddingValues -> ) { paddingValues ->
LazyColumn(modifier = Modifier.padding(paddingValues)) { LazyColumn(
modifier = Modifier.fillMaxSize(),
contentPadding = paddingValues,
) {
item("description") { item("description") {
Text( Text(
text = stringResource(R.string.press_controls_description), text = stringResource(R.string.press_controls_description),
@@ -1,6 +1,6 @@
package eu.darken.capod.main.ui.settings package eu.darken.capod.main.ui.settings
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.twotone.ArrowBack import androidx.compose.material.icons.automirrored.twotone.ArrowBack
@@ -105,7 +105,10 @@ fun SettingsScreen(
) )
}, },
) { innerPadding -> ) { innerPadding ->
LazyColumn(modifier = Modifier.padding(innerPadding)) { LazyColumn(
modifier = Modifier.fillMaxSize(),
contentPadding = innerPadding,
) {
item { item {
SettingsBaseItem( SettingsBaseItem(
title = stringResource(R.string.settings_general_label), title = stringResource(R.string.settings_general_label),
@@ -1,6 +1,6 @@
package eu.darken.capod.main.ui.settings.acks package eu.darken.capod.main.ui.settings.acks
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.twotone.ArrowBack import androidx.compose.material.icons.automirrored.twotone.ArrowBack
@@ -54,7 +54,10 @@ fun AcknowledgementsScreen(
) )
}, },
) { innerPadding -> ) { innerPadding ->
LazyColumn(modifier = Modifier.padding(innerPadding)) { LazyColumn(
modifier = Modifier.fillMaxSize(),
contentPadding = innerPadding,
) {
item { item {
SettingsCategoryHeader(text = stringResource(R.string.general_thank_you_label)) SettingsCategoryHeader(text = stringResource(R.string.general_thank_you_label))
} }
@@ -1,6 +1,7 @@
package eu.darken.capod.main.ui.settings.general package eu.darken.capod.main.ui.settings.general
import android.os.Build import android.os.Build
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
@@ -105,7 +106,10 @@ fun GeneralSettingsScreen(
) )
}, },
) { innerPadding -> ) { innerPadding ->
LazyColumn(modifier = Modifier.padding(innerPadding)) { LazyColumn(
modifier = Modifier.fillMaxSize(),
contentPadding = innerPadding,
) {
item { item {
SettingsCategoryHeader(text = stringResource(R.string.settings_category_appearance_label)) SettingsCategoryHeader(text = stringResource(R.string.settings_category_appearance_label))
} }
@@ -8,6 +8,7 @@ import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
@@ -228,7 +229,10 @@ fun SupportScreen(
) )
}, },
) { innerPadding -> ) { innerPadding ->
LazyColumn(modifier = Modifier.padding(innerPadding)) { LazyColumn(
modifier = Modifier.fillMaxSize(),
contentPadding = innerPadding,
) {
item { item {
SettingsBaseItem( SettingsBaseItem(
title = stringResource(R.string.troubleshooter_title), title = stringResource(R.string.troubleshooter_title),
@@ -9,8 +9,10 @@ import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.FlowRow import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.width
@@ -241,12 +243,14 @@ fun ContactFormScreen(
}, },
) )
}, },
snackbarHost = { SnackbarHost(snackbarHostState) }, snackbarHost = { SnackbarHost(snackbarHostState, modifier = Modifier.imePadding()) },
) { innerPadding -> ) { innerPadding ->
Column( Column(
modifier = Modifier modifier = Modifier
.padding(innerPadding) .fillMaxSize()
.imePadding()
.verticalScroll(rememberScrollState()) .verticalScroll(rememberScrollState())
.padding(innerPadding)
.padding(16.dp), .padding(16.dp),
verticalArrangement = Arrangement.spacedBy(12.dp), verticalArrangement = Arrangement.spacedBy(12.dp),
) { ) {
@@ -22,15 +22,14 @@ import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.FlowRow import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.WindowInsetsSides import androidx.compose.foundation.layout.WindowInsetsSides
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.only import androidx.compose.foundation.layout.only
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.systemBars
import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.windowInsetsPadding import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
@@ -77,6 +76,7 @@ import eu.darken.capod.R
import eu.darken.capod.common.compose.Preview2 import eu.darken.capod.common.compose.Preview2
import eu.darken.capod.common.compose.PreviewWrapper import eu.darken.capod.common.compose.PreviewWrapper
import eu.darken.capod.common.compose.preview.MockPodDataProvider import eu.darken.capod.common.compose.preview.MockPodDataProvider
import eu.darken.capod.common.compose.systemBarsAndCutoutInsets
import eu.darken.capod.pods.core.apple.PodModel import eu.darken.capod.pods.core.apple.PodModel
import eu.darken.capod.profiles.core.DeviceProfile import eu.darken.capod.profiles.core.DeviceProfile
@@ -102,12 +102,13 @@ fun WidgetConfigurationScreen(
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
.windowInsetsPadding(WindowInsets.systemBars.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal)), .imePadding(),
) { ) {
Column( Column(
modifier = Modifier modifier = Modifier
.weight(1f) .weight(1f)
.verticalScroll(rememberScrollState()) .verticalScroll(rememberScrollState())
.windowInsetsPadding(systemBarsAndCutoutInsets.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal))
.padding(top = 24.dp, bottom = 16.dp), .padding(top = 24.dp, bottom = 16.dp),
) { ) {
Text( Text(
@@ -347,7 +348,7 @@ fun WidgetConfigurationScreen(
// Bottom bar // Bottom bar
Surface(tonalElevation = 3.dp) { Surface(tonalElevation = 3.dp) {
Column(modifier = Modifier.windowInsetsPadding(WindowInsets.systemBars.only(WindowInsetsSides.Bottom))) { Column(modifier = Modifier.windowInsetsPadding(systemBarsAndCutoutInsets.only(WindowInsetsSides.Bottom + WindowInsetsSides.Horizontal))) {
if (!state.isPro) { if (!state.isPro) {
Text( Text(
text = stringResource(R.string.common_feature_requires_pro_msg), text = stringResource(R.string.common_feature_requires_pro_msg),
@@ -4,6 +4,7 @@ import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
@@ -44,6 +45,7 @@ import eu.darken.capod.R
import eu.darken.capod.common.compose.Preview2 import eu.darken.capod.common.compose.Preview2
import eu.darken.capod.common.compose.PreviewWrapper import eu.darken.capod.common.compose.PreviewWrapper
import eu.darken.capod.common.compose.ReorderableAutoScroll import eu.darken.capod.common.compose.ReorderableAutoScroll
import eu.darken.capod.common.compose.plus
import eu.darken.capod.common.compose.preview.MockPodDataProvider import eu.darken.capod.common.compose.preview.MockPodDataProvider
import eu.darken.capod.common.compose.reorderableItemModifier import eu.darken.capod.common.compose.reorderableItemModifier
import eu.darken.capod.common.compose.rememberReorderableState import eu.darken.capod.common.compose.rememberReorderableState
@@ -123,9 +125,8 @@ fun DeviceManagerScreen(
val showDragHandles = reorderableState.items.size >= 2 val showDragHandles = reorderableState.items.size >= 2
LazyColumn( LazyColumn(
state = lazyListState, state = lazyListState,
modifier = Modifier modifier = Modifier.fillMaxSize(),
.fillMaxSize() contentPadding = innerPadding + PaddingValues(bottom = 88.dp),
.padding(innerPadding),
) { ) {
itemsIndexed( itemsIndexed(
items = reorderableState.items, items = reorderableState.items,
@@ -8,6 +8,7 @@ import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.width
@@ -199,8 +200,9 @@ fun DeviceProfileCreationScreen(
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
.padding(innerPadding) .imePadding()
.verticalScroll(rememberScrollState()) .verticalScroll(rememberScrollState())
.padding(innerPadding)
.padding(16.dp), .padding(16.dp),
) { ) {
DeviceInfoCard( DeviceInfoCard(
@@ -76,8 +76,8 @@ fun TroubleShooterScreen(
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
.padding(innerPadding) .verticalScroll(rememberScrollState())
.verticalScroll(rememberScrollState()), .padding(innerPadding),
) { ) {
Card( Card(
modifier = Modifier modifier = Modifier
@@ -0,0 +1,33 @@
package eu.darken.capod.common.compose
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
import io.kotest.matchers.shouldBe
import org.junit.jupiter.api.Test
import testhelpers.BaseTest
class InsetsExtensionsTest : BaseTest() {
@Test
fun `plus - sums start-end padding under Ltr`() {
val a = PaddingValues(start = 4.dp, top = 8.dp, end = 12.dp, bottom = 16.dp)
val b = PaddingValues(start = 1.dp, top = 2.dp, end = 3.dp, bottom = 4.dp)
val sum = a + b
sum.calculateLeftPadding(LayoutDirection.Ltr) shouldBe 5.dp
sum.calculateRightPadding(LayoutDirection.Ltr) shouldBe 15.dp
sum.calculateTopPadding() shouldBe 10.dp
sum.calculateBottomPadding() shouldBe 20.dp
}
@Test
fun `plus - sums start-end padding under Rtl`() {
val a = PaddingValues(start = 4.dp, top = 8.dp, end = 12.dp, bottom = 16.dp)
val b = PaddingValues(start = 1.dp, top = 2.dp, end = 3.dp, bottom = 4.dp)
val sum = a + b
sum.calculateLeftPadding(LayoutDirection.Rtl) shouldBe 15.dp
sum.calculateRightPadding(LayoutDirection.Rtl) shouldBe 5.dp
sum.calculateTopPadding() shouldBe 10.dp
sum.calculateBottomPadding() shouldBe 20.dp
}
}