From 38bac1664029afb64e3020ab280fd537484f92e7 Mon Sep 17 00:00:00 2001 From: jeffrey-signal Date: Thu, 14 May 2026 13:47:19 -0400 Subject: [PATCH] Add separate window breakpoints for windows with large widths vs large heights. --- .../securesms/main/MainContentLayoutData.kt | 3 +- .../securesms/window/AppScaffold.kt | 13 +---- .../core/ui/WindowSizeClassExtensions.kt | 53 ++++++++++--------- .../screens/aepentry/EnterAepScreen.kt | 2 +- .../countrycode/CountryCodePickerScreen.kt | 2 +- .../screens/permissions/PermissionsScreen.kt | 2 +- .../phonenumber/PhoneNumberEntryScreen.kt | 2 +- .../RemoteBackupRestoreScreen.kt | 2 +- .../ArchiveRestoreSelectionScreen.kt | 2 +- .../VerificationCodeScreen.kt | 2 +- .../screens/welcome/WelcomeScreen.kt | 2 +- 11 files changed, 39 insertions(+), 46 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/main/MainContentLayoutData.kt b/app/src/main/java/org/thoughtcrime/securesms/main/MainContentLayoutData.kt index 5d53513558..a456f2ff32 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/main/MainContentLayoutData.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/main/MainContentLayoutData.kt @@ -16,7 +16,6 @@ import androidx.compose.ui.platform.LocalResources import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.window.core.layout.WindowSizeClass -import org.signal.core.ui.WindowBreakpoint import org.signal.core.ui.getWindowBreakpoint import org.signal.core.ui.rememberIsSplitPane @@ -79,7 +78,7 @@ data class MainContentLayoutData( val isSplitPane = resources.rememberIsSplitPane() return remember(windowSizeClass, mode, breakpoint, isSplitPane) { - val isLargeWindowSize = breakpoint == WindowBreakpoint.LARGE + val isLargeWindowSize = breakpoint.isLargeWindow MainContentLayoutData( shape = when { diff --git a/app/src/main/java/org/thoughtcrime/securesms/window/AppScaffold.kt b/app/src/main/java/org/thoughtcrime/securesms/window/AppScaffold.kt index 4282d47473..d2414428ce 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/window/AppScaffold.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/window/AppScaffold.kt @@ -61,7 +61,6 @@ import org.signal.core.ui.WindowBreakpoint import org.signal.core.ui.compose.AllDevicePreviews import org.signal.core.ui.compose.Previews import org.signal.core.ui.getWindowBreakpoint -import org.signal.core.ui.isWidthExpanded import org.signal.core.ui.rememberIsSplitPane import org.thoughtcrime.securesms.keyvalue.SignalStore import org.thoughtcrime.securesms.main.MainFloatingActionButtonsCallback @@ -82,16 +81,8 @@ enum class NavigationType { val windowBreakpoint = remember(config) { resources.getWindowBreakpoint() } return when (windowBreakpoint) { - WindowBreakpoint.SMALL -> BAR - WindowBreakpoint.MEDIUM -> { - val windowSizeClass = currentWindowAdaptiveInfo().windowSizeClass - if (windowSizeClass.isWidthExpanded) { - RAIL - } else { - BAR - } - } - WindowBreakpoint.LARGE -> RAIL + WindowBreakpoint.SMALL, WindowBreakpoint.MEDIUM -> BAR + WindowBreakpoint.LARGE_WIDTH, WindowBreakpoint.LARGE_HEIGHT -> RAIL } } } diff --git a/core/ui/src/main/java/org/signal/core/ui/WindowSizeClassExtensions.kt b/core/ui/src/main/java/org/signal/core/ui/WindowSizeClassExtensions.kt index 14f3439a1b..0eeabfb68c 100644 --- a/core/ui/src/main/java/org/signal/core/ui/WindowSizeClassExtensions.kt +++ b/core/ui/src/main/java/org/signal/core/ui/WindowSizeClassExtensions.kt @@ -24,11 +24,14 @@ val WindowSizeClass.detailPaneMaxContentWidth: Dp get() = 624.dp val WindowSizeClass.isWidthCompact get() = !isWidthAtLeastBreakpoint(WindowSizeClass.WIDTH_DP_MEDIUM_LOWER_BOUND) +val WindowSizeClass.isWidthExpanded + get() = isWidthAtLeastBreakpoint(WindowSizeClass.WIDTH_DP_EXPANDED_LOWER_BOUND) + val WindowSizeClass.isHeightCompact get() = !isHeightAtLeastBreakpoint(WindowSizeClass.HEIGHT_DP_MEDIUM_LOWER_BOUND) -val WindowSizeClass.isWidthExpanded - get() = isWidthAtLeastBreakpoint(WindowSizeClass.WIDTH_DP_EXPANDED_LOWER_BOUND) +val WindowSizeClass.isHeightExpanded + get() = isHeightAtLeastBreakpoint(WindowSizeClass.HEIGHT_DP_EXPANDED_LOWER_BOUND) fun Resources.getWindowSizeClass(): WindowSizeClass { return WindowSizeClass.BREAKPOINTS_V1.computeWindowSizeClass( @@ -48,12 +51,11 @@ fun rememberWindowBreakpoint(): WindowBreakpoint { } /** - * Determines the device's form factor (PHONE, FOLDABLE, or TABLET) based on the current - * [Resources] and window size class. + * Determines the device's form factor based on the current [Resources] and window size class. * * This function uses several heuristics: * - Returns [WindowBreakpoint.SMALL] if the width or height is compact - * - Otherwise, falls back to aspect ratio heuristics: wider (≥ 1.5) is [WindowBreakpoint.LARGE], else [WindowBreakpoint.MEDIUM]. + * - Otherwise, falls back to aspect ratio heuristics: wide windows use [WindowBreakpoint.LARGE_WIDTH], tall windows use [WindowBreakpoint.LARGE_HEIGHT], else [WindowBreakpoint.MEDIUM]. * * @return the inferred [WindowBreakpoint] for the current device. */ @@ -68,24 +70,31 @@ fun Resources.getWindowBreakpoint(): WindowBreakpoint { val denominator = minOf(displayMetrics.widthPixels, displayMetrics.heightPixels) val aspectRatio = numerator.toFloat() / denominator - return if (aspectRatio >= TABLET_ASPECT_RATIO) { - WindowBreakpoint.LARGE - } else { - WindowBreakpoint.MEDIUM + return when { + aspectRatio < TABLET_ASPECT_RATIO -> WindowBreakpoint.MEDIUM + else -> { + if (displayMetrics.widthPixels >= displayMetrics.heightPixels) { + WindowBreakpoint.LARGE_WIDTH + } else { + WindowBreakpoint.LARGE_HEIGHT + } + } } } /** * Indicates the general form factor of the device for responsive UI purposes. * - * - [SMALL]: A window similar to a phone-sized device, typically with a compact width or height. - * - [MEDIUM]: A window similar to a foldable or medium-size device, or a device which doesn't obviously fit into phone or tablet by heuristics. - * - [LARGE]: A window similar to a large-screen tablet device, typically with an expanded height or wide aspect ratio. + * - [SMALL]: A window with a compact width or height, typical of phone-sized devices. + * - [MEDIUM]: A window where neither width nor height is compact or expanded, typical of foldables. + * - [LARGE_WIDTH]: A window with expanded width and medium height, typical of tablets in landscape orientation. + * - [LARGE_HEIGHT]: A window with medium width and expanded height, typical of tablets in portrait orientation. */ -enum class WindowBreakpoint { - SMALL, - MEDIUM, - LARGE +enum class WindowBreakpoint(val isLargeWindow: Boolean) { + SMALL(isLargeWindow = false), + MEDIUM(isLargeWindow = false), + LARGE_WIDTH(isLargeWindow = true), + LARGE_HEIGHT(isLargeWindow = true) } @Composable @@ -108,14 +117,8 @@ fun Resources.isSplitPane( return true } - val breakpoint = getWindowBreakpoint() - if (breakpoint == WindowBreakpoint.SMALL) { - return false + return when (getWindowBreakpoint()) { + WindowBreakpoint.SMALL, WindowBreakpoint.LARGE_HEIGHT -> false + WindowBreakpoint.MEDIUM, WindowBreakpoint.LARGE_WIDTH -> true } - - if (breakpoint == WindowBreakpoint.LARGE && displayMetrics.widthPixels < displayMetrics.heightPixels) { - return false - } - - return true } diff --git a/feature/registration/src/main/java/org/signal/registration/screens/aepentry/EnterAepScreen.kt b/feature/registration/src/main/java/org/signal/registration/screens/aepentry/EnterAepScreen.kt index 119cfdb34e..73ff4b3159 100644 --- a/feature/registration/src/main/java/org/signal/registration/screens/aepentry/EnterAepScreen.kt +++ b/feature/registration/src/main/java/org/signal/registration/screens/aepentry/EnterAepScreen.kt @@ -74,7 +74,7 @@ fun EnterAepScreen( MediumLayout(state, onEvent) } - WindowBreakpoint.LARGE -> { + WindowBreakpoint.LARGE_WIDTH, WindowBreakpoint.LARGE_HEIGHT -> { LargeLayout(state, onEvent) } } diff --git a/feature/registration/src/main/java/org/signal/registration/screens/countrycode/CountryCodePickerScreen.kt b/feature/registration/src/main/java/org/signal/registration/screens/countrycode/CountryCodePickerScreen.kt index 988858bcc7..fc1583cd1f 100644 --- a/feature/registration/src/main/java/org/signal/registration/screens/countrycode/CountryCodePickerScreen.kt +++ b/feature/registration/src/main/java/org/signal/registration/screens/countrycode/CountryCodePickerScreen.kt @@ -81,7 +81,7 @@ fun CountryCodePickerScreen( MediumLayout(state, onEvent) } - WindowBreakpoint.LARGE -> { + WindowBreakpoint.LARGE_WIDTH, WindowBreakpoint.LARGE_HEIGHT -> { LargeLayout(state, onEvent) } } diff --git a/feature/registration/src/main/java/org/signal/registration/screens/permissions/PermissionsScreen.kt b/feature/registration/src/main/java/org/signal/registration/screens/permissions/PermissionsScreen.kt index ebb98559c7..a1d6e81c53 100644 --- a/feature/registration/src/main/java/org/signal/registration/screens/permissions/PermissionsScreen.kt +++ b/feature/registration/src/main/java/org/signal/registration/screens/permissions/PermissionsScreen.kt @@ -88,7 +88,7 @@ fun PermissionsScreen( } } - WindowBreakpoint.LARGE -> { + WindowBreakpoint.LARGE_WIDTH, WindowBreakpoint.LARGE_HEIGHT -> { Surface(modifier = modifier.testTag(TestTags.PERMISSIONS_SCREEN)) { LargeLayout( permissionsState = permissionsState, diff --git a/feature/registration/src/main/java/org/signal/registration/screens/phonenumber/PhoneNumberEntryScreen.kt b/feature/registration/src/main/java/org/signal/registration/screens/phonenumber/PhoneNumberEntryScreen.kt index 636c8e30d4..5648936982 100644 --- a/feature/registration/src/main/java/org/signal/registration/screens/phonenumber/PhoneNumberEntryScreen.kt +++ b/feature/registration/src/main/java/org/signal/registration/screens/phonenumber/PhoneNumberEntryScreen.kt @@ -122,7 +122,7 @@ fun PhoneNumberScreen( MediumLayout(state, onEvent) } - WindowBreakpoint.LARGE -> { + WindowBreakpoint.LARGE_WIDTH, WindowBreakpoint.LARGE_HEIGHT -> { LargeLayout(state, onEvent) } } diff --git a/feature/registration/src/main/java/org/signal/registration/screens/remotebackuprestore/RemoteBackupRestoreScreen.kt b/feature/registration/src/main/java/org/signal/registration/screens/remotebackuprestore/RemoteBackupRestoreScreen.kt index 456d114a28..675d437f81 100644 --- a/feature/registration/src/main/java/org/signal/registration/screens/remotebackuprestore/RemoteBackupRestoreScreen.kt +++ b/feature/registration/src/main/java/org/signal/registration/screens/remotebackuprestore/RemoteBackupRestoreScreen.kt @@ -69,7 +69,7 @@ fun RemoteRestoreScreen( when (windowBreakpoint) { WindowBreakpoint.SMALL -> CompactLayout(state = state, onEvent = onEvent, modifier = modifier) WindowBreakpoint.MEDIUM -> MediumLayout(state = state, onEvent = onEvent, modifier = modifier) - WindowBreakpoint.LARGE -> LargeLayout(state = state, onEvent = onEvent, modifier = modifier) + WindowBreakpoint.LARGE_WIDTH, WindowBreakpoint.LARGE_HEIGHT -> LargeLayout(state = state, onEvent = onEvent, modifier = modifier) } } diff --git a/feature/registration/src/main/java/org/signal/registration/screens/restoreselection/ArchiveRestoreSelectionScreen.kt b/feature/registration/src/main/java/org/signal/registration/screens/restoreselection/ArchiveRestoreSelectionScreen.kt index c65f5dcf90..59ac70999c 100644 --- a/feature/registration/src/main/java/org/signal/registration/screens/restoreselection/ArchiveRestoreSelectionScreen.kt +++ b/feature/registration/src/main/java/org/signal/registration/screens/restoreselection/ArchiveRestoreSelectionScreen.kt @@ -72,7 +72,7 @@ fun ArchiveRestoreSelectionScreen( MediumLayout(state, onEvent, modifier) } - WindowBreakpoint.LARGE -> { + WindowBreakpoint.LARGE_WIDTH, WindowBreakpoint.LARGE_HEIGHT -> { LargeLayout(state, onEvent, modifier) } } diff --git a/feature/registration/src/main/java/org/signal/registration/screens/verificationcode/VerificationCodeScreen.kt b/feature/registration/src/main/java/org/signal/registration/screens/verificationcode/VerificationCodeScreen.kt index c61398e4b8..d2cc085c14 100644 --- a/feature/registration/src/main/java/org/signal/registration/screens/verificationcode/VerificationCodeScreen.kt +++ b/feature/registration/src/main/java/org/signal/registration/screens/verificationcode/VerificationCodeScreen.kt @@ -153,7 +153,7 @@ fun VerificationCodeScreen( ) } - WindowBreakpoint.LARGE -> { + WindowBreakpoint.LARGE_WIDTH, WindowBreakpoint.LARGE_HEIGHT -> { LargeLayout( digits = digits, focusRequesters = focusRequesters, diff --git a/feature/registration/src/main/java/org/signal/registration/screens/welcome/WelcomeScreen.kt b/feature/registration/src/main/java/org/signal/registration/screens/welcome/WelcomeScreen.kt index 18e4a280b1..8d856413cf 100644 --- a/feature/registration/src/main/java/org/signal/registration/screens/welcome/WelcomeScreen.kt +++ b/feature/registration/src/main/java/org/signal/registration/screens/welcome/WelcomeScreen.kt @@ -100,7 +100,7 @@ fun WelcomeScreen( ) } - WindowBreakpoint.LARGE -> { + WindowBreakpoint.LARGE_WIDTH, WindowBreakpoint.LARGE_HEIGHT -> { LargeLayout( onEvent = onEvent, onTermsAndPrivacyClick = onTermsAndPrivacyClick,