From 73dc10aee9e589b5144ff0ee3aa3663f64d85562 Mon Sep 17 00:00:00 2001 From: darken Date: Fri, 24 Jul 2026 17:14:45 +0200 Subject: [PATCH] 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. --- .claude/rules/architecture.md | 2 +- .../darken/capod/upgrade/ui/UpgradeScreen.kt | 16 +++- .../darken/capod/upgrade/ui/UpgradeContent.kt | 9 +- .../darken/capod/common/EdgeToEdgeHelper.kt | 34 ------- .../capod/common/compose/InsetsExtensions.kt | 22 +++++ .../capod/common/compose/Reorderable.kt | 8 +- .../debug/recording/ui/RecorderScreen.kt | 88 +++++++++---------- .../ui/devicesettings/DeviceSettingsScreen.kt | 4 +- .../main/ui/onboarding/OnboardingScreen.kt | 4 +- .../capod/main/ui/overview/OverviewScreen.kt | 8 +- .../ui/presscontrols/PressControlsScreen.kt | 6 +- .../capod/main/ui/settings/SettingsScreen.kt | 7 +- .../settings/acks/AcknowledgementsScreen.kt | 7 +- .../settings/general/GeneralSettingsScreen.kt | 6 +- .../main/ui/settings/support/SupportScreen.kt | 6 +- .../support/contactform/ContactFormScreen.kt | 8 +- .../ui/widget/WidgetConfigurationScreen.kt | 9 +- .../capod/profiles/ui/DeviceManagerScreen.kt | 7 +- .../creation/DeviceProfileCreationScreen.kt | 4 +- .../troubleshooter/ui/TroubleShooterScreen.kt | 4 +- .../common/compose/InsetsExtensionsTest.kt | 33 +++++++ 21 files changed, 179 insertions(+), 113 deletions(-) delete mode 100644 app/src/main/java/eu/darken/capod/common/EdgeToEdgeHelper.kt create mode 100644 app/src/main/java/eu/darken/capod/common/compose/InsetsExtensions.kt create mode 100644 app/src/test/java/eu/darken/capod/common/compose/InsetsExtensionsTest.kt diff --git a/.claude/rules/architecture.md b/.claude/rules/architecture.md index db361e8e..d9da7c30 100644 --- a/.claude/rules/architecture.md +++ b/.claude/rules/architecture.md @@ -87,7 +87,7 @@ The monitor-layer glue (`monitor/core/aap/`) described above wires this stack in ### 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 diff --git a/app/src/foss/java/eu/darken/capod/upgrade/ui/UpgradeScreen.kt b/app/src/foss/java/eu/darken/capod/upgrade/ui/UpgradeScreen.kt index e6659e30..4ef757fc 100644 --- a/app/src/foss/java/eu/darken/capod/upgrade/ui/UpgradeScreen.kt +++ b/app/src/foss/java/eu/darken/capod/upgrade/ui/UpgradeScreen.kt @@ -5,11 +5,17 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row 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.height +import androidx.compose.foundation.layout.only import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.safeDrawing import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width +import androidx.compose.foundation.layout.windowInsetsPadding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape @@ -133,10 +139,12 @@ fun SupporterStatusScreen( Scaffold( containerColor = MaterialTheme.colorScheme.surface, ) { paddingValues -> - Box(modifier = Modifier.padding(paddingValues)) { + Box(modifier = Modifier.fillMaxSize()) { Column( modifier = Modifier + .fillMaxWidth() .verticalScroll(rememberScrollState()) + .padding(paddingValues) .padding(horizontal = 24.dp), horizontalAlignment = Alignment.CenterHorizontally, ) { @@ -238,6 +246,7 @@ fun SupporterStatusScreen( onClick = onNavigateUp, modifier = Modifier .align(Alignment.TopStart) + .windowInsetsPadding(WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Start)) .padding(4.dp), ) { Icon( @@ -272,10 +281,12 @@ fun UpgradeScreen( snackbarHost = { SnackbarHost(snackbarHostState) }, containerColor = MaterialTheme.colorScheme.surface, ) { paddingValues -> - Box(modifier = Modifier.padding(paddingValues)) { + Box(modifier = Modifier.fillMaxSize()) { Column( modifier = Modifier + .fillMaxWidth() .verticalScroll(rememberScrollState()) + .padding(paddingValues) .padding(horizontal = 24.dp), horizontalAlignment = Alignment.CenterHorizontally, ) { @@ -402,6 +413,7 @@ fun UpgradeScreen( onClick = onNavigateUp, modifier = Modifier .align(Alignment.TopStart) + .windowInsetsPadding(WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Start)) .padding(4.dp), ) { Icon( diff --git a/app/src/gplay/java/eu/darken/capod/upgrade/ui/UpgradeContent.kt b/app/src/gplay/java/eu/darken/capod/upgrade/ui/UpgradeContent.kt index b152426a..6e4ceceb 100644 --- a/app/src/gplay/java/eu/darken/capod/upgrade/ui/UpgradeContent.kt +++ b/app/src/gplay/java/eu/darken/capod/upgrade/ui/UpgradeContent.kt @@ -8,12 +8,17 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.ColumnScope import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.WindowInsets +import androidx.compose.foundation.layout.WindowInsetsSides import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.only import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.safeDrawing import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.widthIn +import androidx.compose.foundation.layout.windowInsetsPadding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.verticalScroll @@ -105,7 +110,7 @@ internal fun UpgradeScreenContainer( modifier = modifier, containerColor = MaterialTheme.colorScheme.surface, ) { paddingValues -> - Box(modifier = Modifier.padding(paddingValues)) { + Box(modifier = Modifier.fillMaxSize()) { Box( modifier = Modifier .fillMaxSize() @@ -114,6 +119,7 @@ internal fun UpgradeScreenContainer( ) { Column( modifier = Modifier + .padding(paddingValues) // widthIn BEFORE fillMaxWidth: reversed, fillMaxWidth would pin the min to // the full screen and the 560dp cap would never take effect on wide screens. .widthIn(max = 560.dp) @@ -129,6 +135,7 @@ internal fun UpgradeScreenContainer( onClick = onNavigateUp, modifier = Modifier .align(Alignment.TopStart) + .windowInsetsPadding(WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Start)) .padding(4.dp), ) { // Matches capod's app-wide back-button convention (no navigate-up string exists). diff --git a/app/src/main/java/eu/darken/capod/common/EdgeToEdgeHelper.kt b/app/src/main/java/eu/darken/capod/common/EdgeToEdgeHelper.kt deleted file mode 100644 index 431a5b28..00000000 --- a/app/src/main/java/eu/darken/capod/common/EdgeToEdgeHelper.kt +++ /dev/null @@ -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 - } - } -} diff --git a/app/src/main/java/eu/darken/capod/common/compose/InsetsExtensions.kt b/app/src/main/java/eu/darken/capod/common/compose/InsetsExtensions.kt new file mode 100644 index 00000000..7ca0a08c --- /dev/null +++ b/app/src/main/java/eu/darken/capod/common/compose/InsetsExtensions.kt @@ -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) diff --git a/app/src/main/java/eu/darken/capod/common/compose/Reorderable.kt b/app/src/main/java/eu/darken/capod/common/compose/Reorderable.kt index 96359b5d..62c84247 100644 --- a/app/src/main/java/eu/darken/capod/common/compose/Reorderable.kt +++ b/app/src/main/java/eu/darken/capod/common/compose/Reorderable.kt @@ -127,16 +127,16 @@ fun ReorderableAutoScroll(state: ReorderableState) { if (!state.isDragging) return@LaunchedEffect while (isActive) { val layoutInfo = state.lazyListState.layoutInfo - val viewportStart = layoutInfo.viewportStartOffset - val viewportEnd = layoutInfo.viewportEndOffset + val contentStart = layoutInfo.viewportStartOffset + layoutInfo.beforeContentPadding + val contentEnd = layoutInfo.viewportEndOffset - layoutInfo.afterContentPadding val draggedIdx = state.draggedIndex ?: break val draggedItem = layoutInfo.visibleItemsInfo.firstOrNull { it.index == draggedIdx } if (draggedItem != null) { val itemCenter = draggedItem.offset + draggedItem.size / 2 + state.dragOffsetY val scrollSpeed = 8f val delta = when { - itemCenter < viewportStart + scrollThresholdPx -> -scrollSpeed - itemCenter > viewportEnd - scrollThresholdPx -> scrollSpeed + itemCenter < contentStart + scrollThresholdPx -> -scrollSpeed + itemCenter > contentEnd - scrollThresholdPx -> scrollSpeed else -> 0f } if (delta != 0f) state.lazyListState.scrollBy(delta) diff --git a/app/src/main/java/eu/darken/capod/common/debug/recording/ui/RecorderScreen.kt b/app/src/main/java/eu/darken/capod/common/debug/recording/ui/RecorderScreen.kt index 56ee418b..20f62b49 100644 --- a/app/src/main/java/eu/darken/capod/common/debug/recording/ui/RecorderScreen.kt +++ b/app/src/main/java/eu/darken/capod/common/debug/recording/ui/RecorderScreen.kt @@ -8,7 +8,6 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row 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 @@ -16,7 +15,6 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.only import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.systemBars import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.windowInsetsPadding import androidx.compose.foundation.rememberScrollState @@ -54,6 +52,7 @@ import androidx.compose.ui.unit.dp import eu.darken.capod.R import eu.darken.capod.common.compose.Preview2 import eu.darken.capod.common.compose.PreviewWrapper +import eu.darken.capod.common.compose.systemBarsAndCutoutInsets import java.io.File @Composable @@ -68,49 +67,49 @@ fun RecorderScreen( val context = LocalContext.current Box(modifier = modifier.fillMaxSize()) { + Column(modifier = Modifier.fillMaxSize()) { + Column( + modifier = Modifier + .weight(1f) + .verticalScroll(rememberScrollState()) + .windowInsetsPadding(systemBarsAndCutoutInsets.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal)), + ) { + // Hero section + HeroSection() + + Column(modifier = Modifier.padding(horizontal = 16.dp)) { + // Sensitive information card + SensitiveInfoCard(onPrivacyPolicy = onPrivacyPolicy) + + Spacer(modifier = Modifier.height(12.dp)) + + // Session path card + SessionPathCard(path = state.logDir?.path ?: "") + + Spacer(modifier = Modifier.height(16.dp)) + + // Log files section + LogFilesSection( + entries = state.logEntries, + compressedSize = state.compressedSize, + recordingDurationSecs = state.recordingDurationSecs, + context = context, + ) + } + } + + // Bottom action bar + BottomActionBar( + isWorking = state.isWorking, + onDiscard = onDiscard, + onKeep = onKeep, + onShare = onShare, + ) + } + if (state.isWorking) { LinearProgressIndicator(modifier = Modifier.fillMaxWidth()) } - - Column( - modifier = Modifier - .fillMaxSize() - .windowInsetsPadding(WindowInsets.systemBars.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal)) - .verticalScroll(rememberScrollState()) - .padding(bottom = 80.dp), - ) { - // Hero section - HeroSection() - - Column(modifier = Modifier.padding(horizontal = 16.dp)) { - // Sensitive information card - SensitiveInfoCard(onPrivacyPolicy = onPrivacyPolicy) - - Spacer(modifier = Modifier.height(12.dp)) - - // Session path card - SessionPathCard(path = state.logDir?.path ?: "") - - Spacer(modifier = Modifier.height(16.dp)) - - // Log files section - LogFilesSection( - entries = state.logEntries, - compressedSize = state.compressedSize, - recordingDurationSecs = state.recordingDurationSecs, - context = context, - ) - } - } - - // Bottom action bar - BottomActionBar( - isWorking = state.isWorking, - onDiscard = onDiscard, - onKeep = onKeep, - onShare = onShare, - modifier = Modifier.align(Alignment.BottomCenter), - ) } } @@ -373,7 +372,9 @@ private fun BottomActionBar( color = MaterialTheme.colorScheme.surfaceContainerHigh, ) { Row( - modifier = Modifier.padding(12.dp), + modifier = Modifier + .windowInsetsPadding(systemBarsAndCutoutInsets.only(WindowInsetsSides.Bottom + WindowInsetsSides.Horizontal)) + .padding(12.dp), horizontalArrangement = Arrangement.spacedBy(8.dp), ) { OutlinedButton( @@ -415,7 +416,6 @@ private fun BottomActionBar( } } } - Spacer(modifier = Modifier.windowInsetsPadding(WindowInsets.systemBars.only(WindowInsetsSides.Bottom))) } } diff --git a/app/src/main/java/eu/darken/capod/main/ui/devicesettings/DeviceSettingsScreen.kt b/app/src/main/java/eu/darken/capod/main/ui/devicesettings/DeviceSettingsScreen.kt index 0179624f..a2387dd0 100644 --- a/app/src/main/java/eu/darken/capod/main/ui/devicesettings/DeviceSettingsScreen.kt +++ b/app/src/main/java/eu/darken/capod/main/ui/devicesettings/DeviceSettingsScreen.kt @@ -4,6 +4,7 @@ import android.content.Intent import android.provider.Settings import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn @@ -270,7 +271,8 @@ fun DeviceSettingsScreen( snackbarHost = { SnackbarHost(hostState = snackbarHostState) }, ) { paddingValues -> LazyColumn( - modifier = Modifier.padding(paddingValues), + modifier = Modifier.fillMaxSize(), + contentPadding = paddingValues, ) { // Device Info if (device != null) { diff --git a/app/src/main/java/eu/darken/capod/main/ui/onboarding/OnboardingScreen.kt b/app/src/main/java/eu/darken/capod/main/ui/onboarding/OnboardingScreen.kt index f5dc51af..67f91f3c 100644 --- a/app/src/main/java/eu/darken/capod/main/ui/onboarding/OnboardingScreen.kt +++ b/app/src/main/java/eu/darken/capod/main/ui/onboarding/OnboardingScreen.kt @@ -50,9 +50,9 @@ fun OnboardingScreen( Column( modifier = Modifier .fillMaxSize() + .verticalScroll(rememberScrollState()) .padding(innerPadding) - .padding(horizontal = 32.dp) - .verticalScroll(rememberScrollState()), + .padding(horizontal = 32.dp), horizontalAlignment = Alignment.CenterHorizontally, ) { Spacer(modifier = Modifier.height(48.dp)) diff --git a/app/src/main/java/eu/darken/capod/main/ui/overview/OverviewScreen.kt b/app/src/main/java/eu/darken/capod/main/ui/overview/OverviewScreen.kt index 0ad67d7b..331a74df 100644 --- a/app/src/main/java/eu/darken/capod/main/ui/overview/OverviewScreen.kt +++ b/app/src/main/java/eu/darken/capod/main/ui/overview/OverviewScreen.kt @@ -6,7 +6,6 @@ import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items 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.compose.Preview2 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.error.ErrorEventHandler import eu.darken.capod.common.navigation.NavigationEventHandler @@ -278,10 +278,8 @@ fun OverviewScreen( snackbarHost = { SnackbarHost(hostState = snackbarHostState) }, ) { innerPadding -> LazyColumn( - modifier = Modifier - .fillMaxSize() - .padding(innerPadding), - contentPadding = PaddingValues(horizontal = 8.dp, vertical = 4.dp), + modifier = Modifier.fillMaxSize(), + contentPadding = innerPadding + PaddingValues(horizontal = 8.dp, vertical = 4.dp), ) { // 1. Permission cards items( diff --git a/app/src/main/java/eu/darken/capod/main/ui/presscontrols/PressControlsScreen.kt b/app/src/main/java/eu/darken/capod/main/ui/presscontrols/PressControlsScreen.kt index 271ef619..a1cd6121 100644 --- a/app/src/main/java/eu/darken/capod/main/ui/presscontrols/PressControlsScreen.kt +++ b/app/src/main/java/eu/darken/capod/main/ui/presscontrols/PressControlsScreen.kt @@ -1,6 +1,7 @@ package eu.darken.capod.main.ui.presscontrols import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn @@ -176,7 +177,10 @@ fun PressControlsScreen( ) }, ) { paddingValues -> - LazyColumn(modifier = Modifier.padding(paddingValues)) { + LazyColumn( + modifier = Modifier.fillMaxSize(), + contentPadding = paddingValues, + ) { item("description") { Text( text = stringResource(R.string.press_controls_description), diff --git a/app/src/main/java/eu/darken/capod/main/ui/settings/SettingsScreen.kt b/app/src/main/java/eu/darken/capod/main/ui/settings/SettingsScreen.kt index cdcc762e..92ddad96 100644 --- a/app/src/main/java/eu/darken/capod/main/ui/settings/SettingsScreen.kt +++ b/app/src/main/java/eu/darken/capod/main/ui/settings/SettingsScreen.kt @@ -1,6 +1,6 @@ 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.material.icons.Icons import androidx.compose.material.icons.automirrored.twotone.ArrowBack @@ -105,7 +105,10 @@ fun SettingsScreen( ) }, ) { innerPadding -> - LazyColumn(modifier = Modifier.padding(innerPadding)) { + LazyColumn( + modifier = Modifier.fillMaxSize(), + contentPadding = innerPadding, + ) { item { SettingsBaseItem( title = stringResource(R.string.settings_general_label), diff --git a/app/src/main/java/eu/darken/capod/main/ui/settings/acks/AcknowledgementsScreen.kt b/app/src/main/java/eu/darken/capod/main/ui/settings/acks/AcknowledgementsScreen.kt index 8505357d..8c3988fc 100644 --- a/app/src/main/java/eu/darken/capod/main/ui/settings/acks/AcknowledgementsScreen.kt +++ b/app/src/main/java/eu/darken/capod/main/ui/settings/acks/AcknowledgementsScreen.kt @@ -1,6 +1,6 @@ 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.material.icons.Icons import androidx.compose.material.icons.automirrored.twotone.ArrowBack @@ -54,7 +54,10 @@ fun AcknowledgementsScreen( ) }, ) { innerPadding -> - LazyColumn(modifier = Modifier.padding(innerPadding)) { + LazyColumn( + modifier = Modifier.fillMaxSize(), + contentPadding = innerPadding, + ) { item { SettingsCategoryHeader(text = stringResource(R.string.general_thank_you_label)) } diff --git a/app/src/main/java/eu/darken/capod/main/ui/settings/general/GeneralSettingsScreen.kt b/app/src/main/java/eu/darken/capod/main/ui/settings/general/GeneralSettingsScreen.kt index c58c79bc..7fed44f2 100644 --- a/app/src/main/java/eu/darken/capod/main/ui/settings/general/GeneralSettingsScreen.kt +++ b/app/src/main/java/eu/darken/capod/main/ui/settings/general/GeneralSettingsScreen.kt @@ -1,6 +1,7 @@ package eu.darken.capod.main.ui.settings.general import android.os.Build +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.material.icons.Icons @@ -105,7 +106,10 @@ fun GeneralSettingsScreen( ) }, ) { innerPadding -> - LazyColumn(modifier = Modifier.padding(innerPadding)) { + LazyColumn( + modifier = Modifier.fillMaxSize(), + contentPadding = innerPadding, + ) { item { SettingsCategoryHeader(text = stringResource(R.string.settings_category_appearance_label)) } diff --git a/app/src/main/java/eu/darken/capod/main/ui/settings/support/SupportScreen.kt b/app/src/main/java/eu/darken/capod/main/ui/settings/support/SupportScreen.kt index 41c8f37e..e45a6b39 100644 --- a/app/src/main/java/eu/darken/capod/main/ui/settings/support/SupportScreen.kt +++ b/app/src/main/java/eu/darken/capod/main/ui/settings/support/SupportScreen.kt @@ -8,6 +8,7 @@ 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.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding @@ -228,7 +229,10 @@ fun SupportScreen( ) }, ) { innerPadding -> - LazyColumn(modifier = Modifier.padding(innerPadding)) { + LazyColumn( + modifier = Modifier.fillMaxSize(), + contentPadding = innerPadding, + ) { item { SettingsBaseItem( title = stringResource(R.string.troubleshooter_title), diff --git a/app/src/main/java/eu/darken/capod/main/ui/settings/support/contactform/ContactFormScreen.kt b/app/src/main/java/eu/darken/capod/main/ui/settings/support/contactform/ContactFormScreen.kt index 275e2764..56373ae5 100644 --- a/app/src/main/java/eu/darken/capod/main/ui/settings/support/contactform/ContactFormScreen.kt +++ b/app/src/main/java/eu/darken/capod/main/ui/settings/support/contactform/ContactFormScreen.kt @@ -9,8 +9,10 @@ import androidx.compose.foundation.layout.ExperimentalLayoutApi import androidx.compose.foundation.layout.FlowRow import androidx.compose.foundation.layout.Row 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.imePadding import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width @@ -241,12 +243,14 @@ fun ContactFormScreen( }, ) }, - snackbarHost = { SnackbarHost(snackbarHostState) }, + snackbarHost = { SnackbarHost(snackbarHostState, modifier = Modifier.imePadding()) }, ) { innerPadding -> Column( modifier = Modifier - .padding(innerPadding) + .fillMaxSize() + .imePadding() .verticalScroll(rememberScrollState()) + .padding(innerPadding) .padding(16.dp), verticalArrangement = Arrangement.spacedBy(12.dp), ) { diff --git a/app/src/main/java/eu/darken/capod/main/ui/widget/WidgetConfigurationScreen.kt b/app/src/main/java/eu/darken/capod/main/ui/widget/WidgetConfigurationScreen.kt index 94611602..fd64f8c2 100644 --- a/app/src/main/java/eu/darken/capod/main/ui/widget/WidgetConfigurationScreen.kt +++ b/app/src/main/java/eu/darken/capod/main/ui/widget/WidgetConfigurationScreen.kt @@ -22,15 +22,14 @@ import androidx.compose.foundation.layout.ExperimentalLayoutApi import androidx.compose.foundation.layout.FlowRow import androidx.compose.foundation.layout.Row 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.height +import androidx.compose.foundation.layout.imePadding import androidx.compose.foundation.layout.only import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.systemBars import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.windowInsetsPadding 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.PreviewWrapper 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.profiles.core.DeviceProfile @@ -102,12 +102,13 @@ fun WidgetConfigurationScreen( Column( modifier = Modifier .fillMaxSize() - .windowInsetsPadding(WindowInsets.systemBars.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal)), + .imePadding(), ) { Column( modifier = Modifier .weight(1f) .verticalScroll(rememberScrollState()) + .windowInsetsPadding(systemBarsAndCutoutInsets.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal)) .padding(top = 24.dp, bottom = 16.dp), ) { Text( @@ -347,7 +348,7 @@ fun WidgetConfigurationScreen( // Bottom bar 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) { Text( text = stringResource(R.string.common_feature_requires_pro_msg), diff --git a/app/src/main/java/eu/darken/capod/profiles/ui/DeviceManagerScreen.kt b/app/src/main/java/eu/darken/capod/profiles/ui/DeviceManagerScreen.kt index 0168c924..3874b3af 100644 --- a/app/src/main/java/eu/darken/capod/profiles/ui/DeviceManagerScreen.kt +++ b/app/src/main/java/eu/darken/capod/profiles/ui/DeviceManagerScreen.kt @@ -4,6 +4,7 @@ import androidx.compose.foundation.Image import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer 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.PreviewWrapper 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.reorderableItemModifier import eu.darken.capod.common.compose.rememberReorderableState @@ -123,9 +125,8 @@ fun DeviceManagerScreen( val showDragHandles = reorderableState.items.size >= 2 LazyColumn( state = lazyListState, - modifier = Modifier - .fillMaxSize() - .padding(innerPadding), + modifier = Modifier.fillMaxSize(), + contentPadding = innerPadding + PaddingValues(bottom = 88.dp), ) { itemsIndexed( items = reorderableState.items, diff --git a/app/src/main/java/eu/darken/capod/profiles/ui/creation/DeviceProfileCreationScreen.kt b/app/src/main/java/eu/darken/capod/profiles/ui/creation/DeviceProfileCreationScreen.kt index 32d81b82..c2121ec4 100644 --- a/app/src/main/java/eu/darken/capod/profiles/ui/creation/DeviceProfileCreationScreen.kt +++ b/app/src/main/java/eu/darken/capod/profiles/ui/creation/DeviceProfileCreationScreen.kt @@ -8,6 +8,7 @@ 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.imePadding import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width @@ -199,8 +200,9 @@ fun DeviceProfileCreationScreen( Column( modifier = Modifier .fillMaxSize() - .padding(innerPadding) + .imePadding() .verticalScroll(rememberScrollState()) + .padding(innerPadding) .padding(16.dp), ) { DeviceInfoCard( diff --git a/app/src/main/java/eu/darken/capod/troubleshooter/ui/TroubleShooterScreen.kt b/app/src/main/java/eu/darken/capod/troubleshooter/ui/TroubleShooterScreen.kt index a8f08909..b0e4471f 100644 --- a/app/src/main/java/eu/darken/capod/troubleshooter/ui/TroubleShooterScreen.kt +++ b/app/src/main/java/eu/darken/capod/troubleshooter/ui/TroubleShooterScreen.kt @@ -76,8 +76,8 @@ fun TroubleShooterScreen( Column( modifier = Modifier .fillMaxSize() - .padding(innerPadding) - .verticalScroll(rememberScrollState()), + .verticalScroll(rememberScrollState()) + .padding(innerPadding), ) { Card( modifier = Modifier diff --git a/app/src/test/java/eu/darken/capod/common/compose/InsetsExtensionsTest.kt b/app/src/test/java/eu/darken/capod/common/compose/InsetsExtensionsTest.kt new file mode 100644 index 00000000..0a3030c6 --- /dev/null +++ b/app/src/test/java/eu/darken/capod/common/compose/InsetsExtensionsTest.kt @@ -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 + } +}