From 6563f1bbebc732b1db30922f904dcfabbf9a4f29 Mon Sep 17 00:00:00 2001 From: Philipp Hasper Date: Wed, 3 Jun 2026 17:10:40 +0200 Subject: [PATCH] Fix system gesture triggering when editing quad corners. On devices without a back button (e.g. Pixel10), dragging corners when close to the screen borders triggered the system back gesture. Adding exclusion areas for each of the corner handles fixes this. It was not possible to exclude the whole image area, as Android seems to silently limit the area of allowed exclusion. The mandatory system gestures like the home or quick-switch gesture cannot be excluded via areas. The only solution here is to introduce a bottom padding which moves the image view upwards, out of the gesture area. Both changes are no-ops for devices without system gestures, so no change there. Closes #193 --- .../app/ui/screens/crop/CropScreen.kt | 6 ++++ .../app/ui/screens/crop/QuadOverlay.kt | 28 ++++++++++++++++++- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/org/fairscan/app/ui/screens/crop/CropScreen.kt b/app/src/main/java/org/fairscan/app/ui/screens/crop/CropScreen.kt index 1818abb..cc907b5 100644 --- a/app/src/main/java/org/fairscan/app/ui/screens/crop/CropScreen.kt +++ b/app/src/main/java/org/fairscan/app/ui/screens/crop/CropScreen.kt @@ -27,6 +27,7 @@ import androidx.compose.foundation.gestures.waitForUpOrCancellation import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.mandatorySystemGestures import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.safeDrawing import androidx.compose.foundation.layout.windowInsetsPadding @@ -81,6 +82,10 @@ fun CropScreen( BackHandler { navigation.back() } val isLandscape = isLandscape(LocalConfiguration.current) + val density = LocalDensity.current + val bottomInsetForSystemGestures = with(density) { + WindowInsets.mandatorySystemGestures.getBottom(density).toDp() + } MyScaffold( navigation = navigation, @@ -102,6 +107,7 @@ fun CropScreen( contentDescription = "Image to edit", modifier = Modifier .fillMaxSize() + .padding(bottom = bottomInsetForSystemGestures) .onGloballyPositioned { coordinates -> state.containerSize = coordinates.size }, diff --git a/app/src/main/java/org/fairscan/app/ui/screens/crop/QuadOverlay.kt b/app/src/main/java/org/fairscan/app/ui/screens/crop/QuadOverlay.kt index d5fd193..437e5af 100644 --- a/app/src/main/java/org/fairscan/app/ui/screens/crop/QuadOverlay.kt +++ b/app/src/main/java/org/fairscan/app/ui/screens/crop/QuadOverlay.kt @@ -16,14 +16,18 @@ package org.fairscan.app.ui.screens.crop import androidx.compose.foundation.Canvas import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.systemGestureExclusion import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.geometry.Rect import androidx.compose.ui.unit.IntSize import org.fairscan.imageprocessing.Point import org.fairscan.imageprocessing.Quad import org.fairscan.imageprocessing.scaledTo +import kotlin.math.max +import kotlin.math.min @Composable fun QuadOverlay( @@ -35,7 +39,15 @@ fun QuadOverlay( val quadColor = MaterialTheme.colorScheme.primary val handleColor = quadColor.copy(alpha = 0.5f) - Canvas(modifier = modifier.fillMaxSize()) { + Canvas(modifier = modifier + .fillMaxSize() + // Android limits the exclusion areas, so we cannot simply exclude the whole quad/image. + // Instead, the corners must get excluded individually. + .systemGestureExclusion { cornerExclusionRect(quad.topLeft, containerSize, displaySize) } + .systemGestureExclusion { cornerExclusionRect(quad.topRight, containerSize, displaySize) } + .systemGestureExclusion { cornerExclusionRect(quad.bottomRight, containerSize, displaySize) } + .systemGestureExclusion { cornerExclusionRect(quad.bottomLeft, containerSize, displaySize) } + ) { val scaledQuad = quad.scaledTo( fromWidth = 1, fromHeight = 1, @@ -74,3 +86,17 @@ fun QuadOverlay( } fun Point.toOffset() = Offset(x.toFloat(), y.toFloat()) + +private fun cornerExclusionRect( + corner: Point, + containerSize: IntSize, + displaySize: IntSize +): Rect { + val pos = QuadCoordinateUtils.normalizedToScreen(corner, containerSize, displaySize) + val r = QuadEditingHandler.CORNER_TOUCH_RADIUS + return Rect(max(.0f, pos.x - r), + max(.0f, pos.y - r), + min(containerSize.width.toFloat(), pos.x + r), + min(containerSize.height.toFloat(), pos.y + r) + ) +}