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 + } +}