From 4c43bf2228adf545425f41bd23ba3cb846a49527 Mon Sep 17 00:00:00 2001 From: jeffrey-signal Date: Thu, 22 Jan 2026 13:57:09 -0500 Subject: [PATCH] Replace categorical window size classes with breakpoint-based checks. --- .../components/compose/ScreenTitlePane.kt | 5 ++-- .../webrtc/v2/CallScreenPreJoinOverlay.kt | 4 +-- .../InlineQueryResultsControllerV2.kt | 4 +-- .../ConversationListFragment.java | 6 ++-- .../securesms/main/MainContentLayoutData.kt | 26 +++++++++-------- .../securesms/main/MainMegaphoneContainer.kt | 6 ++-- .../window/WindowSizeClassExtensions.kt | 28 +++++++++---------- 7 files changed, 40 insertions(+), 39 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/compose/ScreenTitlePane.kt b/app/src/main/java/org/thoughtcrime/securesms/components/compose/ScreenTitlePane.kt index 7965d18c62..57fab9255d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/compose/ScreenTitlePane.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/compose/ScreenTitlePane.kt @@ -12,8 +12,7 @@ import androidx.compose.material3.adaptive.currentWindowAdaptiveInfo import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp -import androidx.window.core.layout.WindowWidthSizeClass -import org.thoughtcrime.securesms.window.isAtLeast +import androidx.window.core.layout.WindowSizeClass /** * Displays the screen title for split-pane UIs on tablets and foldable devices. @@ -31,7 +30,7 @@ fun ScreenTitlePane( color = MaterialTheme.colorScheme.onSurface, modifier = modifier .padding( - start = if (windowSizeClass.windowWidthSizeClass.isAtLeast(WindowWidthSizeClass.EXPANDED)) 80.dp else 20.dp, + start = if (windowSizeClass.isWidthAtLeastBreakpoint(WindowSizeClass.WIDTH_DP_EXPANDED_LOWER_BOUND)) 80.dp else 20.dp, end = 20.dp, bottom = 12.dp ) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/v2/CallScreenPreJoinOverlay.kt b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/v2/CallScreenPreJoinOverlay.kt index 86143c1c17..63275c9a1c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/v2/CallScreenPreJoinOverlay.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/v2/CallScreenPreJoinOverlay.kt @@ -44,7 +44,6 @@ import androidx.compose.ui.res.vectorResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp -import androidx.window.core.layout.WindowWidthSizeClass import org.signal.core.ui.compose.AllNightPreviews import org.signal.core.ui.compose.NightPreview import org.signal.core.ui.compose.Previews @@ -55,6 +54,7 @@ import org.thoughtcrime.securesms.events.CallParticipant import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.recipients.rememberRecipientField import org.thoughtcrime.securesms.service.webrtc.links.CallLinkRoomId +import org.thoughtcrime.securesms.window.isWidthCompact import kotlin.math.max /** @@ -85,7 +85,7 @@ fun CallScreenPreJoinOverlay( .background(color = Color(0f, 0f, 0f, 0.4f)) .then(modifier) ) { - val isCompactWidth = currentWindowAdaptiveInfo().windowSizeClass.windowWidthSizeClass == WindowWidthSizeClass.COMPACT + val isCompactWidth = currentWindowAdaptiveInfo().windowSizeClass.isWidthCompact if (!isLocalVideoEnabled) { TopWithCenteredContentLayout( diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ui/inlinequery/InlineQueryResultsControllerV2.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/ui/inlinequery/InlineQueryResultsControllerV2.kt index 27bb3df409..62b28706f3 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ui/inlinequery/InlineQueryResultsControllerV2.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ui/inlinequery/InlineQueryResultsControllerV2.kt @@ -6,7 +6,6 @@ import androidx.fragment.app.Fragment import androidx.fragment.app.commit import androidx.lifecycle.DefaultLifecycleObserver import androidx.lifecycle.LifecycleOwner -import androidx.window.core.layout.WindowHeightSizeClass import androidx.window.core.layout.WindowSizeClass import io.reactivex.rxjava3.kotlin.subscribeBy import org.signal.core.util.DimensionUnit @@ -17,6 +16,7 @@ import org.thoughtcrime.securesms.components.ComposeText import org.thoughtcrime.securesms.conversation.ui.mentions.MentionsPickerFragmentV2 import org.thoughtcrime.securesms.util.adapter.mapping.AnyMappingModel import org.thoughtcrime.securesms.util.doOnEachLayout +import org.thoughtcrime.securesms.window.isHeightCompact /** * Controller for inline search results. @@ -73,7 +73,7 @@ class InlineQueryResultsControllerV2( } fun onWindowSizeClassChanged(windowSizeClass: WindowSizeClass) { - this.shouldHideForWindowSizeClass = windowSizeClass.windowHeightSizeClass == WindowHeightSizeClass.COMPACT + this.shouldHideForWindowSizeClass = windowSizeClass.isHeightCompact if (shouldHideForWindowSizeClass) { dismiss() diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListFragment.java b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListFragment.java index 5907a989bb..a21e2533ce 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListFragment.java @@ -53,7 +53,6 @@ import androidx.recyclerview.widget.ItemTouchHelper; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearSmoothScroller; import androidx.recyclerview.widget.RecyclerView; -import androidx.window.core.layout.WindowHeightSizeClass; import com.airbnb.lottie.SimpleColorFilter; import com.annimon.stream.Stream; @@ -162,6 +161,7 @@ import org.thoughtcrime.securesms.util.adapter.mapping.PagingMappingAdapter; import org.thoughtcrime.securesms.components.SignalProgressDialog; import org.thoughtcrime.securesms.util.views.Stub; import org.thoughtcrime.securesms.wallpaper.ChatWallpaper; +import org.thoughtcrime.securesms.window.WindowSizeClassExtensionsKt; import org.whispersystems.signalservice.api.websocket.WebSocketConnectionState; import java.lang.ref.WeakReference; @@ -317,7 +317,7 @@ public class ConversationListFragment extends MainFragment implements Conversati searchAdapter = contactSearchMediator.getAdapter(); - if (getWindowSizeClass(getResources()).getWindowHeightSizeClass() == WindowHeightSizeClass.COMPACT) { + if (WindowSizeClassExtensionsKt.isHeightCompact(getWindowSizeClass(getResources()))) { ViewUtil.setBottomMargin(bottomActionBar, ViewUtil.getNavigationBarHeight(bottomActionBar)); } @@ -392,7 +392,7 @@ public class ConversationListFragment extends MainFragment implements Conversati initializeVoiceNotePlayer(); initializeBanners(); maybeScheduleRefreshProfileJob(); - ConversationListFragmentExtensionsKt.listenToEventBusWhileResumed(this, mainNavigationViewModel .getDetailLocation()); + ConversationListFragmentExtensionsKt.listenToEventBusWhileResumed(this, mainNavigationViewModel.getDetailLocation()); String query = contactSearchMediator.getFilter(); if (query != null) { 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 4d31c7c580..e426bc297b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/main/MainContentLayoutData.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/main/MainContentLayoutData.kt @@ -14,8 +14,7 @@ import androidx.compose.ui.graphics.RectangleShape import androidx.compose.ui.graphics.Shape import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp -import androidx.window.core.layout.WindowWidthSizeClass -import org.thoughtcrime.securesms.window.isAtLeast +import androidx.window.core.layout.WindowSizeClass import org.thoughtcrime.securesms.window.isSplitPane private val MEDIUM_CONTENT_CORNERS = 18.dp @@ -58,7 +57,7 @@ data class MainContentLayoutData( return remember(maxWidth, windowSizeClass) { when { !windowSizeClass.isSplitPane() -> maxWidth - windowSizeClass.windowWidthSizeClass.isAtLeast(WindowWidthSizeClass.EXPANDED) -> 416.dp + windowSizeClass.isWidthAtLeastBreakpoint(WindowSizeClass.WIDTH_DP_EXPANDED_LOWER_BOUND) -> 416.dp else -> (maxWidth - extraPadding) / 2f } } @@ -73,24 +72,27 @@ data class MainContentLayoutData( val windowSizeClass = currentWindowAdaptiveInfo().windowSizeClass return remember(windowSizeClass, mode) { + val isSplitPane = windowSizeClass.isSplitPane() + val isWidthExpanded = windowSizeClass.isWidthAtLeastBreakpoint(WindowSizeClass.WIDTH_DP_EXPANDED_LOWER_BOUND) + MainContentLayoutData( shape = when { - !windowSizeClass.isSplitPane() -> RectangleShape - windowSizeClass.windowWidthSizeClass.isAtLeast(WindowWidthSizeClass.EXPANDED) -> RoundedCornerShape(EXTENDED_CONTENT_CORNERS) + !isSplitPane -> RectangleShape + isWidthExpanded -> RoundedCornerShape(EXTENDED_CONTENT_CORNERS) else -> RoundedCornerShape(MEDIUM_CONTENT_CORNERS) }, navigationBarShape = when { - !windowSizeClass.isSplitPane() -> RectangleShape - windowSizeClass.windowWidthSizeClass.isAtLeast(WindowWidthSizeClass.EXPANDED) -> RoundedCornerShape(0.dp, 0.dp, EXTENDED_CONTENT_CORNERS, EXTENDED_CONTENT_CORNERS) + !isSplitPane -> RectangleShape + isWidthExpanded -> RoundedCornerShape(0.dp, 0.dp, EXTENDED_CONTENT_CORNERS, EXTENDED_CONTENT_CORNERS) else -> RoundedCornerShape(0.dp, 0.dp, MEDIUM_CONTENT_CORNERS, MEDIUM_CONTENT_CORNERS) }, partitionWidth = when { - !windowSizeClass.isSplitPane() -> 0.dp - windowSizeClass.windowWidthSizeClass.isAtLeast(WindowWidthSizeClass.EXPANDED) -> 24.dp + !isSplitPane -> 0.dp + isWidthExpanded -> 24.dp else -> 13.dp }, listPaddingStart = when { - !windowSizeClass.isSplitPane() -> 0.dp + !isSplitPane -> 0.dp else -> { when (mode) { MainToolbarMode.SEARCH -> 24.dp @@ -99,8 +101,8 @@ data class MainContentLayoutData( } }, detailPaddingEnd = when { - !windowSizeClass.isSplitPane() -> 0.dp - windowSizeClass.windowWidthSizeClass.isAtLeast(WindowWidthSizeClass.EXPANDED) -> 24.dp + !isSplitPane -> 0.dp + isWidthExpanded -> 24.dp else -> 12.dp } ) diff --git a/app/src/main/java/org/thoughtcrime/securesms/main/MainMegaphoneContainer.kt b/app/src/main/java/org/thoughtcrime/securesms/main/MainMegaphoneContainer.kt index f3fa4c634f..c7e501ca95 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/main/MainMegaphoneContainer.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/main/MainMegaphoneContainer.kt @@ -13,13 +13,13 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.remember import androidx.fragment.app.DialogFragment -import androidx.window.core.layout.WindowHeightSizeClass import org.signal.core.ui.compose.DayNightPreviews import org.signal.core.ui.compose.Previews import org.thoughtcrime.securesms.megaphone.Megaphone import org.thoughtcrime.securesms.megaphone.MegaphoneActionController import org.thoughtcrime.securesms.megaphone.MegaphoneComponent import org.thoughtcrime.securesms.megaphone.Megaphones +import org.thoughtcrime.securesms.window.isHeightCompact data class MainMegaphoneState( val megaphone: Megaphone = Megaphone.NONE, @@ -47,7 +47,7 @@ fun MainMegaphoneContainer( ) { val windowSizeClass = currentWindowAdaptiveInfo().windowSizeClass val visible = remember(windowSizeClass, state) { - !(state.megaphone == Megaphone.NONE || state.mainToolbarMode != MainToolbarMode.FULL || windowSizeClass.windowHeightSizeClass == WindowHeightSizeClass.COMPACT) + !(state.megaphone == Megaphone.NONE || state.mainToolbarMode != MainToolbarMode.FULL || windowSizeClass.isHeightCompact) } AnimatedVisibility(visible = visible) { @@ -58,7 +58,7 @@ fun MainMegaphoneContainer( } LaunchedEffect(state, windowSizeClass) { - if (state.megaphone == Megaphone.NONE || state.mainToolbarMode == MainToolbarMode.BASIC || windowSizeClass.windowHeightSizeClass == WindowHeightSizeClass.COMPACT) { + if (state.megaphone == Megaphone.NONE || state.mainToolbarMode == MainToolbarMode.BASIC || windowSizeClass.isHeightCompact) { return@LaunchedEffect } diff --git a/app/src/main/java/org/thoughtcrime/securesms/window/WindowSizeClassExtensions.kt b/app/src/main/java/org/thoughtcrime/securesms/window/WindowSizeClassExtensions.kt index f9141be2bb..f593167ab8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/window/WindowSizeClassExtensions.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/window/WindowSizeClassExtensions.kt @@ -8,27 +8,25 @@ package org.thoughtcrime.securesms.window import android.content.res.Resources import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp -import androidx.window.core.ExperimentalWindowCoreApi -import androidx.window.core.layout.WindowHeightSizeClass import androidx.window.core.layout.WindowSizeClass -import androidx.window.core.layout.WindowWidthSizeClass +import androidx.window.core.layout.computeWindowSizeClass import org.thoughtcrime.securesms.keyvalue.SignalStore -val WindowSizeClass.listPaneDefaultPreferredWidth: Dp get() = if (windowWidthSizeClass.isAtLeast(WindowWidthSizeClass.EXPANDED)) 416.dp else 316.dp +val WindowSizeClass.listPaneDefaultPreferredWidth: Dp get() = if (isWidthAtLeastBreakpoint(WindowSizeClass.WIDTH_DP_EXPANDED_LOWER_BOUND)) 416.dp else 316.dp val WindowSizeClass.horizontalPartitionDefaultSpacerSize: Dp get() = 12.dp val WindowSizeClass.detailPaneMaxContentWidth: Dp get() = 624.dp -fun WindowHeightSizeClass.isAtLeast(other: WindowHeightSizeClass): Boolean { - return hashCode() >= other.hashCode() -} +val WindowSizeClass.isWidthCompact + get() = !isWidthAtLeastBreakpoint(WindowSizeClass.WIDTH_DP_MEDIUM_LOWER_BOUND) -fun WindowWidthSizeClass.isAtLeast(other: WindowWidthSizeClass): Boolean { - return hashCode() >= other.hashCode() -} +val WindowSizeClass.isHeightCompact + get() = !isHeightAtLeastBreakpoint(WindowSizeClass.HEIGHT_DP_MEDIUM_LOWER_BOUND) -@OptIn(ExperimentalWindowCoreApi::class) fun Resources.getWindowSizeClass(): WindowSizeClass { - return WindowSizeClass.compute(displayMetrics.widthPixels / displayMetrics.density, displayMetrics.heightPixels / displayMetrics.density) + return WindowSizeClass.BREAKPOINTS_V1.computeWindowSizeClass( + widthDp = displayMetrics.widthPixels / displayMetrics.density, + heightDp = displayMetrics.heightPixels / displayMetrics.density + ) } /** @@ -42,6 +40,8 @@ fun WindowSizeClass.isSplitPane( return true } - return windowWidthSizeClass.isAtLeast(WindowWidthSizeClass.EXPANDED) && - windowHeightSizeClass.isAtLeast(WindowHeightSizeClass.MEDIUM) + return isAtLeastBreakpoint( + widthDpBreakpoint = WindowSizeClass.WIDTH_DP_EXPANDED_LOWER_BOUND, + heightDpBreakpoint = WindowSizeClass.HEIGHT_DP_MEDIUM_LOWER_BOUND + ) }